Rails: How to pass instance variables from a controller to render partial - ruby-on-rails

I am having trouble passing an instance variable (#article) from a controller (articles_controller.rb) to a partial render (_form.html.erb) in Ruby.
Here is the error from being sent back:
`undefined method `errors' for nil:NilClass`
articles_controller.rb:
class ArticlesController < ApplicationController
def new
end
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
render 'new'
end
end
def update
#article = Article.find(params[:id])
if #article.update(article_params)
redirect_to #article
else
render 'edit', :article => #article
end
end
def show
#article = Article.find(params[:id])
end
def index
#articles = Article.all
end
private
def article_params
params.require(:article).permit(:title, :text)
end
end
new.html.erb
<h1>New Article</h1>
<%= form_for :article, url: articles_path do |f|%>
<%= render partial: "form", :locals => {:article => #article} %>
<% end %>
<% link_to 'Back', articles_path %>
_form.html.erb
<% if #article.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#article.errors.count, "error") %> prohibited this
article from being saved:
</h2>
<ul>
<% #article.errors.full_messeages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :title %><br />
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br />
<%= f.text_area :text %>
</p>
<p>
<%= f.submit %>
</p>
Any help would be appreciated

def new
#article = Article.new
end
<%= render partial: "form", collection: #article %>
or even
<%= form_for #article do |f|%>
<%= render 'form' %>
<% end %>
depends on your needs

I had this challenge when working on a Rails 6 application.
I wanted to use an instance variable in a partial (app/views/shared/_header.html.erb) that was defined in a different controller (app/controllers/categories_controller.rb).
Here's how I did it:
The instance variable that I wanted to use is #categories which is defined as:
# app/controllers/categories_controller.rb
class CategoriesController < ApplicationController
def index
#categories = Category.all
end
.
.
.
end
Firstly, I rendered the app/views/shared/_header.html.erb partial in the app/views/layouts/application.html.erb and passed the #categories instance variable into it this way:
<%= render partial: '/shared/header', locals: { categories: #categories } %>
And then I used the instance variable in the partial this way:
# app/views/shared/_header.html.erb
<% #categories.each do |category| %>
<%= link_to category do %>
<%= category.name %>
<% end %>
<% end %>
However, this will require a controller action that sets a #categories instance variable for every controller views that will use the partial.
If you want to make variables globally available in your controllers and views, this could help: Rails: Set a common or global instance variable across several controller actions
That's all.
I hope this helps

You should do like this, removing #. You are passing it to local variable article so you could access it with article not #article:
<% if article.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(article.errors.count, "error") %> prohibited this
article from being saved:
</h2>
<ul>
<% article.errors.full_messeages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :title %><br />
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br />
<%= f.text_area :text %>
</p>
<p>
<%= f.submit %>
</p>
For more info, please take a look at passing-local-variables

Related

Why is the object nil in this view?

I want to create a new article object, but have this error:
Showing
/Users/levanngoc/code/blog_app/app/views/articles/new.html.erb where
line #3 raised:
undefined method `errors' for nil:NilClass
How can I fix it?
this is my controller:
class ArticlesController < ApplicationController
def index
#article = Article.all
end
def show
#article = Article.find(params[:id])
end
def new
end
def edit
end
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
render 'new'
end
end
def update
end
def destroy
end
private
def article_params
params.require(:article).permit(:title, :text)
end
end
this is my view file: new.html.erb
<h1>New Article</h1>
<%= form_with scope: :article,url: articles_path , local: true do |form| %>
<% if #article.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#article.errors.count, "error") %>
prohibited this article from being saved:
</h2>
<ul>
<% #article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= form.label :title %><br>
<%= form.text_field :title %>
</p>
<p>
<%= form.label :text %><br>
<%= form.text_area :text %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
<%= link_to 'Back', articles_path %>
Your new view uses #article but the new action in the controller doesn't create this instance variable. You must change it to:
def new
#article = Article.new
end
You must declare:
#article = Article.new

Error not displaying with form_with

Good Morning.
I'm following the tutorial http://edgeguides.rubyonrails.org/getting_started.html and I find the following:
I have a controller called ArticlesController. The create method uses the "if # article.save" statement to save the #article object, and if something goes wrong then render 'new'. Similarly the update method uses "if #article.update (article_params)" to update the record for that article, and if something goes wrong render 'edit'.
In new and edit views <% if # article.errors.any? %> is used to determine if there was any error and display the corresponding message, but the problem is that in the new view works fine, but in the edit view #article.errors.any view? returns false even if there is an error.
Can someone tell me what's wrong. Thank you very much.
Then I put the ArticlesController class, the views new.html.erb and edit.html.erb, and the article model.
class ArticlesController < ApplicationController
def new
#article = Article.new
end
def edit
#article = Article.find(params[:id])
end
def update
#article = Article.find(params[:id])
if #article.update(article_params)
redirect_to #article
else
render 'edit'
end
end
def index
#articles = Article.all
end
def show
#article = Article.find(params[:id])
end
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
render 'new'
end
end
private
def article_params
params.require(:article).permit(:title, :text)
end
end
------------------------------------------------------------------
<h1>New Article</h1>
<%= form_with scope: :article, url: articles_path, local: true do |form| %>
<% if #article.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#article.errors.count, "error") %> prohibited
this article from being saved:
</h2>
<ul>
<% #article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= form.label :title %><br>
<%= form.text_field :title %>
</p>
<p>
<%= form.label :text %><br>
<%= form.text_area :text %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
<%= link_to 'Back', articles_path %>
----------------------------------------------------------------------
<h1>Edit article</h1>
<%= form_with(model: #article) do |form| %>
<% if #article.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#article.errors.count, "error") %> prohibited
this article from being saved:
</h2>
<ul>
<% #article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= form.label :title %><br>
<%= form.text_field :title %>
</p>
<p>
<%= form.label :text %><br>
<%= form.text_area :text %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
<%= link_to 'Back', articles_path %>
---------------------------------------------------------------------
class Article < ApplicationRecord
validates :title, presence: true,
length: { minimum: 5 }
validates :text, presence: true
end
All forms generated by form_with will be submitted by an XHR (Ajax) request by default. if you want to disable remote forms then you can do it with local like you did in the form in new.html.erb.
change this in your edit.html.erb :
<%= form_with model: #article, local: true do |form| %>
It's a limitation of Turbolinks, which does not work with render
See here https://joey.io/turbolinks-rendering-form-errors/ and here https://github.com/jorgemanrubia/turbolinks_render
Got the same error today. They are using render with form_with in their documentation but it doesn't work.
I used form_for and it fixed the problem.
<%= form_for article do |form| %>

Getting "param is missing or the value is empty: post" explained below

when i click new post and try to save a new post it gives me that error, then i go to the controller :
private
def posts_params
params.require(:post).permit(:title, :description)
end
and change 'require(:post)' to 'require(:posts' then i works
but then i try to edit the new post i just created and when i click to save it it gives me the same error, then i just change it back to 'required(:post)' and it works, why this is happening ? it's like a loop, if one works the other doesn't and to work i have to change that one thing
Controller:
class PostsController < ApplicationController
def index
#posts = Post.all
end
def edit
#posts = Post.find(params[:id])
end
def update
#posts = Post.find(params[:id])
if #posts.update(posts_params)
redirect_to #posts
else
render 'edit'
end
end
def new
#posts = Post.new
end
def create
#posts = Post.new(posts_params)
if #posts.save
redirect_to #posts
else
render 'new'
end
end
def show
#posts = Post.find(params[:id])
end
private
def posts_params
params.require(:post).permit(:title, :description)
end
end
view edit:
<h1>Editing post</h1>
<%= form_for(#posts) do |f| %>
<% if #posts.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#posts.errors.count, "error") %> prohibited
this post from being saved:
</h2>
<ul>
<% #posts.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :description %><br>
<%= f.text_area :description %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
<%= link_to 'Back', posts_path %>
view new:
<h1>New Article</h1>
<%= form_for :posts, url: posts_path do |f| %>
<% if #posts.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#posts.errors.count, "error") %> prohibited
this post from being saved:
</h2>
<ul>
<% #posts.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :description %><br>
<%= f.text_area :description %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
<%= link_to 'Back', posts_path %>
can someone point the problem out ?
You are mixing
form_for(#posts) do |f|
and
form_for :posts, url: posts_path
In your forms.
the :posts version will generate params[:posts] and the #posts version will generate params[:post]. Hence the issue you are seeing. Make sure you posts_params is as follows.
def posts_params
params.require(:post).permit(:title, :description)
end
then just change both of your forms to be
<%= form_for(#posts) do |f| %>
rails will figure out which to call automatically for you, so you will not have to specify the paths..
On a side note, I would probably change #posts to be #post everywhere but the index action, just so that it makes more sense, Since in new,edit,etc.. you are dealing with a singular post.
Since rails is looking at the Model/class of the variable when generating the routes (When given an instance variable) the name of the variable doesn't matter to the framework, but makes it easier (in my opinion) for the programmer to understand

Rails Getting Started Tutorial partial not woking for new blog post

I'm working my way through the rails "Getting Started" tutorial, and I'm stuck at the point where they introduce partials. For some reason the partial doesn't work as described when rendered from new.html.erb, although it does work when rendered from edit.html.erb. When clicking "New" to get to the new.html.erb, I'm getting the following error:
Error:
"First argument in form cannot contain nil or be empty"
for the first line in the following partial:
_form.html.erb:
<%= form_for #post do |f| %>
<% if #post.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#post.errors.count, "error") %> prohibited
this article from being saved:</h2>
<ul>
<% #post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :title %><br/>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br/>
<%= f.textarea :text %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
new.html.erb:
<h1>New Post</h1>
<%= render 'form' %>
<%= link_to 'Back', posts_path %>
edit.html.erb:
<h1>Edit post</h1>
<%= render 'form' %>
<%= link_to 'Back', posts_path %>
posts_controller.rb:
...
def new
end
...
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
if #post.update(post_params)
redirect_to #post
else
render 'edit'
end
end
...
It looks as though the new.html.erb doesn't know about the #post variable if it's a new post. The original new.html.erb looked like this:
<h1>New Post</h1>
<%= form_for :post, url: posts_path do |f| %>
...
... other than the use of symbols instead of #post, it's identical to the partial form.
Any ideas?
You don't set #post instance variable in your Posts#new action (and that's why it's nil). You should have:
def new
#post = Post.new
end
in your PostsController.

rails 3 associations error

I have a table pages and a table author. Each page belongs to one author. Created migrations for the tables and models as well. But getting this error while using it in form:
NoMethodError in Pages#new
Showing C:/rorapp/app/views/pages/_form.html.erb where line #1 raised:
undefined method `build' for nil:NilClass
Extracted source (around line #1):
1: <%= form_for(#page, #page.author.build) do |f| %>
2: <% if #page.errors.any? %>
3: <div id="error_explanation">
4: <h2><%= pluralize(#page.errors.count, "error") %> prohibited this post from being saved:</h2>
Trace of template inclusion: app/views/pages/new.html.erb
Rails.root: C:/rorapp
Application Trace | Framework Trace | Full Trace
app/views/pages/_form.html.erb:1:in `_app_views_pages__form_html_erb___998576952_54480300'
app/views/pages/new.html.erb:2:in `_app_views_pages_new_html_erb__638997451_40207104'
Request
Parameters:
None
Show session dump
Show env dump
Response
Headers:
None
Here is my model file for author:
class Author < ActiveRecord::Base
has_one :page
end
And page model:
class Page < ActiveRecord::Base
validates :title, :presence => true
belongs_to :author
end
And here is the snippet of model form :
<%= form_for(#page, #page.author.build) do |f| %>
<% if #page.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#page.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% #page.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :title %><br />
<%= f.text_field :title %>
</p>
Any ideas how to go ahead with it?
thanks
EDIT - 1
Here is my action method called new :
def new
#page = Page.new
#page.build_author
end
And here is the form it is rendering:
<%= form_for(#page) do |f| %>
<% if #page.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#page.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% #page.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :title %><br />
<%= f.text_field :title %>
</p>
<p>
<%= f.label :body %><br />
<%= f.text_area :body %>
</p>
<p>
<%= f.label :author %><br />
<%= f.text_field :author %>
</p>
<p>
<%= f.label :email %><br />
<%= f.text_field :email %>
</p>
<p>
<%= f.label :reference %><br />
<%= f.select(:reference,[['google',1],['yahoo',2],['MSN',3],['Ask',4]]) %>
</p>
<%= f.submit "Submit" %>
<% end %>
EDIT - 2
Here is my controller code:
class PagesController < ApplicationController
def index
#total = Page.count
#pages = Page.find(:all)
end
def show
#page = Page.find(params[:id])
end
def new
#page = Page.new
#page.build_author
end
def create
#page = Page.new(params[:page])
if #page.save
redirect_to pages_path, :notice => "The data has been saved!"
else
render "new"
end
end
def edit
#page = Page.find(params[:id])
end
def update
#page = Page.find(params[:id])
if #page.update_attributes(params[:page])
redirect_to pages_path, :notice => "Your post has been updated!"
else
render "edit"
end
end
def destroy
#page = Page.find(params[:id])
#page.destroy
redirect_to pages_path, :notice => "Your page has been deleted!"
end
end
you need to add *#page.build_author* in the new action of pages controller.
def new
#page = Page.new
#page.build_author
respond_to do |format|
format.html # new.html.erb
format.json { render json: #page }
end
end
It should be #page.build_author instead of #page.author.build. But the logic of this still doesn't look right to me.

Resources