I am following a rails tutorial, in the signup form, if invalid user info is submitted, the signup page is supposed to be re-rendered with error messages, but actually it doesn't. It seems that even if the signup page is rendered by "render 'new'", the #user passed to it is empty. How to fix this?
Please note that the tutorial uses Rails 6 but I'm actually using Rails 7.0.2.3 with Ruby 3.1.1. Not sure if this is the cause.
app/controllers/users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
else
render 'new'
end
end
def show
#user = User.find(params[:id])
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
end
app/views/users/new.html.erb
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_with(model: #user, local: true) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit "Create my account", class: "btn btn-primary" %>
<% end %>
</div>
</div>
app/views/shared/_error_messages.html.erb
<% if #user.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
The form contains <%= pluralize(#user.errors.count, "error") %>.
</div>
<ul>
<% #user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
app/assets/stylesheets/custom.scss
#import "bootstrap-sprockets";
#import "bootstrap";
/* variables */
$gray-medium-light: #eaeaea;
/* mixins */
#mixin box_sizing {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
/* miscellaneous */
.debug_dump {
clear: both;
float: left;
width: 100%;
margin-top: 45px;
#include box_sizing;
}
/* universal */
body {
padding-top: 60px;
}
section {
overflow: auto;
}
textarea {
resize: vertical;
}
.center {
text-align: center;
h1 {
margin-bottom: 10px;
}
}
/* typography */
h1, h2, h3, h4, h5, h6 {
line-height: 1;
}
h1 {
font-size: 3em;
letter-spacing: -2px;
margin-bottom: 30px;
text-align: center;
}
h2 {
font-size: 1.2em;
letter-spacing: -1px;
margin-bottom: 30px;
text-align: center;
font-weight: normal;
color: $gray-light;
}
p {
font-size: 1.1em;
line-height: 1.7em;
}
/* header */
#logo {
float: left;
margin-right: 10px;
font-size: 1.7em;
color: white;
text-transform: uppercase;
letter-spacing: -1px;
padding-top: 9px;
font-weight: bold;
&:hover {
color: white;
text-decoration: none;
}
}
/* footer */
footer {
margin-top: 45px;
padding-top: 5px;
border-top: 1px solid $gray-medium-light;
color: $gray-light;
a {
color: $gray;
&:hover {
color: $gray-darker;
}
}
small {
float: left;
}
ul {
float: right;
list-style: none;
li {
float: left;
margin-left: 15px;
}
}
}
/* sidebar */
aside {
section.user_info {
margin-top: 20px;
}
section {
padding: 10px 0;
margin-top: 20px;
&:first-child {
border: 0;
padding-top: 0;
}
span {
display: block;
margin-bottom: 3px;
line-height: 1;
}
h1 {
font-size: 1.4em;
text-align: left;
letter-spacing: -1px;
margin-bottom: 3px;
margin-top: 0px;
}
}
}
.gravatar {
float: left;
margin-right: 10px;
}
.gravatar_edit {
margin-top: 15px;
}
/* forms */
input, textarea, select, .uneditable-input {
border: 1px solid #bbb;
width: 100%;
margin-bottom: 15px;
#include box_sizing;
}
input {
height: auto !important;
}
#error_explanation {
color: red;
ul {
color: red;
margin: 0 0 30px 0;
}
}
.field_with_errors {
#extend .has-error;
.form-control {
color: $state-danger-text;
}
}
Debug info of rails server when form submit button is clicked with invalid
Started POST "/users" for ::1 at 2022-04-06 00:55:04
Processing by UsersController#create as TURBO_STREAM
Parameters: {"authenticity_token"=>"[FILTERED]", "user"=>{"name"=>"foo123", "email"=>"foo123#asdf", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Create my account"}
TRANSACTION (0.0ms) begin transaction
↳ app/controllers/users_controller.rb:8:in `create'
User Exists? (0.1ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = ? LIMIT ? [["email", "foo123#asdf"], ["LIMIT", 1]]
↳ app/controllers/users_controller.rb:8:in `create'
TRANSACTION (0.0ms) rollback transaction
↳ app/controllers/users_controller.rb:8:in `create'
Rendering layout layouts/application.html.erb
Rendering users/new.html.erb within layouts/application
Rendered shared/_error_messages.html.erb (Duration: 0.4ms | Allocations: 403)
Rendered users/new.html.erb within layouts/application (Duration: 1.9ms | Allocations: 1891)
Rendered layouts/_shim.html.erb (Duration: 0.0ms | Allocations: 15)
Rendered layouts/_header.html.erb (Duration: 0.1ms | Allocations: 78)
Rendered layouts/_footer.html.erb (Duration: 0.1ms | Allocations: 51)
Rendered layout layouts/application.html.erb (Duration: 9.5ms | Allocations: 8695)
Completed 200 OK in 213ms (Views: 9.8ms | ActiveRecord: 0.2ms | Allocations: 11628)
In rails 7 forms are submitting as TURBO_STREAM by default. After submitting a form Turbo expects a redirect unless response status is in 400-599 range.
render :new # default status is 200
With status code 200 Turbo shows an error in the browser console and page doesn't re-render.
To make Turbo accept rendered html, change the response status. Default seems to be :unprocessable_entity (status code 422)
render :new, status: :unprocessable_entity
https://turbo.hotwired.dev/handbook/drive#redirecting-after-a-form-submission
https://github.com/hotwired/turbo/commit/4670f2b57c5d0246dfc0f6d10ff7d9a52a63fdca
Update: a note on "Content-Type". This applies to default form submission with turbo.
In this set up turbo is expecting an html response with Content-Type: text/html;. #puerile noted that omitting .html extension in your views also breaks the response.
Rails uses .html extension to set response content type to text/html. When extension is omitted content type is set to text/vnd.turbo-stream.html because the form is submitted as TURBO_STREAM, since our response doesn't have a <turbo-stream> it is the wrong content type.
>> Mime[:turbo_stream].to_str
=> "text/vnd.turbo-stream.html"
If we have a view views/users/new.erb, this will not work:
if #user.save
redirect_to #user
else
# NOTE: this will render `new.erb` and set
# `Content-Type: text/vnd.turbo-stream.html` header;
# turbo is not happy.
render :new, status: :unprocessable_entity
end
To fix it, use respond_to method:
respond_to do |format|
if #user.save
format.html { redirect_to #user }
else
# NOTE: this will render `new.erb` and set
# `Content-Type: text/html` header;
# turbo is happy.
format.html { render(:new, status: :unprocessable_entity) }
end
end
or set content type manually:
if #user.save
redirect_to #user
else
render :new, status: :unprocessable_entity, content_type: "text/html"
# NOTE: you can also set headers like this
headers["Content-Type"] = "text/html"
end
One caveat with the last set up is that the layout has to be without .html extension as well, otherwise, render :new will render new.erb without a layout and turbo won't be happy again. This is not an issue when using respond_to method.
https://api.rubyonrails.org/classes/ActionController/MimeResponds.html#method-i-respond_to
If you look at the logs you can see that Rails is getting an AJAX request in the form of a turbo stream:
Processing by UsersController#create as TURBO_STREAM
Where it should read:
Processing by UsersController#create as HTML
To disable turbo you want need to set a data-turbo="false" attribute on the form:
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_with(model: #user, data: { turbo: false }) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit "Create my account", class: "btn btn-primary" %>
<% end %>
</div>
</div>
The local: false option only works with the old Rails UJS javascript library which was the default prior to Rails 7. You can also disable Turbo by default with:
import { Turbo } from "#hotwired/turbo-rails"
Turbo.session.drive = false
See:
https://turbo.hotwired.dev/handbook/drive#disabling-turbo-drive-on-specific-links-or-forms
Related
I'm using Action Text in order to create articles and display well the content of it.
But I have a problem, when I create my article then display the content it's showing the HTML tags.
Here :
Here is my code :
articles/show.html.erb :
<%= #article.body %>
application.scss:
#import "trix/dist/trix";
#import "./actiontext.scss";
_form.html.erb:
<%= form_for #article do |f| %>
<%= f.rich_text_area :body, height: "300px", width:"300px" %>
<%= end %>
application.js :
//= require actiontext
require("trix")
require("#rails/actiontext")
actiontext.scss:
.trix-content {
.attachment-gallery {
> action-text-attachment,
> .attachment {
flex: 1 0 33%;
padding: 0 0.5em;
max-width: 33%;
}
&.attachment-gallery--2,
&.attachment-gallery--4 {
> action-text-attachment,
> .attachment {
flex-basis: 50%;
max-width: 50%;
}
}
}
action-text-attachment {
.attachment {
padding: 0 !important;
max-width: 100% !important;
}
}
}
Does anyone got any idea ?
Well I don't know why action text are showing the HTML balise but maybe using SanitizeHelper would help you to remove the balises.
We use it in our project after having a similar problem.
I run this rake:
task monthly_report: :environment do
User.all.each do |user|
if user.challenges.present? && user.challenges.any?
UserMailer.monthly_report(user).deliver_now
end
end
end
Now maybe in the each method itself is there a way to not send to user's who don't have any challenges?
I keep getting this error:
ActionView::Template::Error: undefined method 'challenges' for nil:NilClassafter I run in productionheroku run rake monthly_report
A user has_many challenges and challenges belongs_to user.
user_mailer.rb
def monthly_report(user)
#user = user if user.email.present?
mail to: user.email, subject: "Sorry! Please Ignore | Testing Email - Monthly Challenges Report"
end
monthly_report.html.erb
Accomplished: <%= #user.challenges.accomplished.count %> Ongoing: <%= #user.challenges.unaccomplished.count %>
<% if #user.challenges.accomplished.present? %>
<% #user.challenges.accomplished.order("deadline ASC").each do |challenge| %>
<% if challenge.date_started != nil %>
<%= link_to challenge_url(challenge) do %>
<%= challenge.name %> <%= challenge.committed_description %> for <%= challenge.days_challenged %> Days
<% end %>
<% if challenge.deadline.present? %>
<span style="display: inline; padding: .2em .6em .3em; font-size: 75%; font-weight: bold; line-height: 1; color: #fff; text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: .25em; background-color: #446CB3; text-decoration: none;"><%= challenge.deadline.strftime("%b %d, %Y") %></span>
<% end %>
<% else %>
<%= link_to challenge_url(challenge) do %>
<%= challenge.name %>
<% end %>
<% if challenge.deadline.present? %>
<span style="display: inline; padding: .2em .6em .3em; font-size: 75%; font-weight: bold; line-height: 1; color: #fff; text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: .25em; background-color: #446CB3; text-decoration: none;"><%= challenge.deadline.strftime("%b %d, %Y") %></span>
<% end %>
<% end %>
<% end %>
<% else %>
None.
<% end %>
Please update your rake task
task monthly_report: :environment do
User.all.each do |user|
if user.email.present? && user.challenges.present? && user.challenges.any?
UserMailer.monthly_report(user).deliver_now
end
end
end
I think error raising if your user dose not have any email in th record try this
Hi all I use simple form. I have a model which contains attachment. Everything works fine except for I get a browse button in browser as shown in below image. Instead of Browse button I want bootstrap attachment glyphicon. How can I achieve this?. My code is below this image:
<%= simple_form_for Status.new do |f| %>
<%= f.input :status, as: :text, required: true, autofocus: true %>
<%= f.input :statachment, as: :file, label: 'Attach here' %>
<%= f.submit "Post", class: 'btn btn-primary' %>
<% end %>
You can try the following ways
http://markusslima.github.io/bootstrap-filestyle/
just need to add
<script type="text/javascript" src="js/bootstrap-filestyle.min.js"> </script>
Then
$(":file").filestyle({input: false});
or
<%= f.input :statachment, as: :file, label: 'Attach here', class: "filestyle", data-input: "false" %>
Or using another way
http://www.abeautifulsite.net/whipping-file-inputs-into-shape-with-bootstrap-3/
In Views
<span class="btn btn-default btn-file">
Browse <%= f.input :statachment, as: :file, label: 'Attach here' %>
</span>
and in css
.btn-file {
position: relative;
overflow: hidden;
}
.btn-file input[type=file] {
position: absolute;
top: 0;
right: 0;
min-width: 100%;
min-height: 100%;
font-size: 100px;
text-align: right;
filter: alpha(opacity=0);
opacity: 0;
outline: none;
background: white;
cursor: inherit;
display: block;
}
I have been working with Michael Hartl's tutorial and I am almost finished. However, I got near the end and am have a problem styling the micropost section of the "user/show/" page. I would like all the microposts to be under one another. Currently they are showing like this:
custom.css.scss
.microposts {
list-style: none;
padding: 0;
li {
padding: 10px 0;
border-top: 1px solid #e8e8e8;
}
.user {
margin-top: 5em;
padding-top: 0;
}
.content {
display: block;
margin-left: 60px;
img {
display: block;
padding: 5px 0;
}
}
.timestamp {
color: $gray-light;
display: block;
margin-left: 60px;
}
.gravatar {
float: left;
margin-right: 10px;
margin-top: 5px;
}
}
aside {
textarea {
height: 100px;
margin-bottom: 5px;
}
}
span.picture {
margin-top: 10px;
input {
border: 0;
}
}
microposts.html.erb
<li id="micropost-<%= micropost.id %>">
<%= link_to gravatar_for(micropost.user), micropost.user %>
<span class="user"><%= link_to micropost.user.name, micropost.user %></span>
<span class="content"><%= micropost.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
</span>
</li>
show.html.erb
<% provide(:title, #user.name) %>
<div class="row">
<aside class="col-md-4">
<section class="user_info">
<h1>
<%= gravatar_for #user %>
<%= #user.name %>
</h1>
</section>
</aside>
<div class="col-md-8">
<% if #user.microposts.any? %>
<h3>Microposts (<%= #user.microposts.count %>)</h3>
<ol class="microposts">
<%= render #microposts %>
</ol>
<%= will_paginate #microposts %>
<% end %>
</div>
</div>
If someone could please help me, it would be highly appreciated. Thank you in advance!
You need to clear the floats. Try adding clear: both; to your .microposts .li SASS
.microposts {
list-style: none;
padding: 0;
li {
padding: 10px 0;
border-top: 1px solid #e8e8e8;
clear: both;
}
JSFIDDLE DEMO
I have a partial with the functionality of "tell a friend about something".
And I want to let the user know if everything went ok so I return in my controller to show the flashes.
The problem is I can see the flashes via pry (debugger) but on screen nothing is shown.
As shown below:
Pry Results:
**IMPORTANT:**Tell a friend is a page that is opened when clicking on a link. The link opens an index page which opens a partial.
I've tried different things:
return render :index
render :index
redirect
...
The index.html.erb file:
<!doctype html>
<html lang="nl">
<head>
<meta charset="utf-8" />
<title><%= t("share.index.header") %></title>
<!--[if lt IE 9]><script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
<%= javascript_include_tag "application" %>
<%= stylesheet_link_tag "application", :media => "all" %>
<style>
body {
margin: 2em;
font: 14px/1em 'open-sans', sans-serif;
color: #333;
background: #eee;
}
h1 {
font-weight: normal;
color: #666;
}
p {
color: #666;
font-size: 12px;
margin: 0 0 3em 0;
}
p a {
color: #333;
text-decoration: none;
border-bottom: 1px dotted #666;
}
form {
position: relative;
}
.col-2 {
position: absolute;
top: 0; left: 25em;
}
label {
display: block;
margin: 0 0 2em 0;
}
strong {
font-weight: normal;
display: block;
margin: 0 0 .5em 0;
}
input[type=text] {
padding: 6px;
width: 25em;
border: 1px solid #ccc;
background: #fff;
}
input[type=submit] {
color: white;
font-size: 14px;
padding: .75em;
}
</style>
</head>
<body>
<div id="wrap">
<% if current_user %>
<%= render :partial => "signed_in_user" %>
<% else %>
<%= render :partial => "anonymous_user" %>
<% end %>
</div>
<script>
</script>
</body>
</html>
The signed_in_user partial:
<script type="text/javascript"charset="UTF-8">
$(document).ready(function() {
$( "#submitBtn" ).click(function() {
// Simple input Validation
var emailaddress = $( "#email").val();
if( !validateEmail(emailaddress)) {
$( "#validationerror").show();
return false;
};
});
function validateEmail($email) {
var emailReg = /^([\w-\.]+#([\w-]+\.)+[\w-]{2,4})?$/;
if ($email == '' ){
return false;
} else {
if( !emailReg.test( $email ) ) {
return false;
} else {
return true;
}
};
};
});
</script>
<!-- Why is this code not showing after return render -->
<!-- Why is this code not showing after return render -->
<!-- Why is this code not showing after return render -->
<p><%= flash[:notice] %></p>
<!-- Why is this code not showing after return render -->
<!-- Why is this code not showing after return render -->
<!-- Why is this code not showing after return render -->
<h1><%= t :header, :scope => [:share, :index] %></h1>
<p><%= t :link_to_page_html, :scope => [:share, :index], :link => link_to(truncate(job_url(params[:job_id]),length: 50, omission: '...'), job_path(params[:job_id])) %></p>
<%= form_tag(job_share_index_path(params[:job_id]), :method => "post", :id => "myform", :remote => true) do %>
<%= hidden_field_tag "guid", params[:job_id] %>
<div class="col-1">
<label>
<strong><%= t("share.index.name") %></strong>
<%= text_field_tag "fullname", "", :autofocus => "autofocus" %>
</label>
<label>
<strong><%= t("share.index.email") %></strong>
<%= content_tag(:p, t("share.index.valid_email") , :id => "validationerror", :class => 'small_label' , :style => "display: none;color: red; font-weight: bold;") %>
<%= text_field_tag "email", "", :placeholder => t("share.index.email_placeholder"), :id => "email" %>
</label>
</div> <!-- /.col-1 -->
<div class="col-2">
<label>
<strong><%= t("share.index.message") %></strong>
<%= text_area_tag "message", "", :style => "width: 370px; height: 175px;"%>
</label>
</div> <!-- /.col-1 -->
<%= submit_tag t("share.index.button"), :class => "medium button", :id => "submitBtn" %>
<% end %>
The share controller
class ShareController < ApplicationController
def index
render :layout => false
end
def create
Sap.tell_a_friend(params[:fullname], params[:message], params[:guid], params[:email], api_params_for_user)
# respond_to do |format|
# format.html { redirect_to(job_share_index_path) }
# format.js
# end
flash[:notice] ="test"
return render :index
end
end
flash is intended for messages that should be visible on the next action/page view/request. You are attempting to render the flash in the same action as you set it, that's not going to work.
You need to use flash.now to set the flash messages:
This method enables you to use the flash as a central messaging system in your app. When you need to pass an object to the next action, you use the standard flash assign ([]=). 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.
ie:
flash.now[:notice] ="test" # or flash.now.notice = "test"
Fetch your Flash like this:
<%= content_tag :div, flash[:notice] %>
So for starters I want to thank Zendist(Andreas Bjørn) and Jakob S.
I was thinking since it's only tell a friend function the only real validation that needs to happen is check email.
So instead of returning a flash message via rails I just return a message to the user via javascript if his email address is valid.
As shown here:
$( "#submitBtn" ).click(function() {
// Simple input Validation
var emailaddress = $( "#email").val();
if( !validateEmail(emailaddress)) {
$( "#validationerror").show();
return false;
} else {
$( "#validationnotice").text("hello world");
};
});
Thanks again for your input and help #greatcommunity!