Render simple_form from partial view on application.html.erb - ruby-on-rails

I want to create a partial view for my registration form and add it to the application layout file because it will be shown in the navigation bar in a dropdown menu.
How can I create this form in a partial view using simple_form gem and render it on application.html.erb?
<%= simple_form_for(#user, url: account_register_path) do |f| %>
Considering that the code above is the way to create the form and I don't know where I should define #user to be used in the application layout nor if I really need it.
Can someone please clarify this?

Don't put it in a partial, just have the registration view on its own, called with render...
#app/views/layouts/application.html.erb
<%= render "accounts/new" %>
#app/views/accounts/new.html.erb
<%= simple_form_for :user, url: account_register_path do |f| %>
...
Whilst you can use a symbol to populate form_for, it won't include the attributes of the model, or the various hidden methods which give context (such as id etc).
If you wanted to populate the accounts#new view/action with a variable, you'll have to set it in your ApplicationController:
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :set_user
private
def set_user
#user = User.new
end
end
This would allow you to use:
#app/views/accounts/new.html.erb
<%= simple_form_for #user, url: account_register_path do |f| %>
--
The best way to implement this would be with Ajax -- this way, the #user object is only created when you need it (resolves bloat).
We've done this before:
You'd just need the following:
#app/views/layouts/application.html.erb
<%= link_to "New User", account_register_path, remote: true %>
#app/controllers/accounts_controller.rb
class AccountsController < ApplicationController
respond_to :js, :html
def new
#user = User.new
respond_with #user
end
end
#app/views/accounts/new.js.erb
$("<%=j render 'accounts/new' %>").appendTo("body");

You have to define #user in the corresponding controller action (I would assume from your code, that controller is called account and action is register), it would be something like:
#user = User.new
In order to initialize instance being created/updated. Then, attributes used in all fields should match users database table.

Related

Rails form used in different partial, or how to reuse method

I have a ContactsController that looks like:
class ContactsController < ApplicationController
def create
#contact = Contact.new(contact_params)
if captcha?
ContactMailer.potential_lead(#contact).deliver if #contact.save
else
#contact.errors.add(:base, 'Verify your humanity')
end
respond_with #contact
end
def thanks
#contact = 'success'
render file: '/layouts/contact'
end
private
def contact_params
params.require(:contact).permit(:name, :message, :interest, :email, :phone)
end
end
There are no issues with the ContactsController, views, mailers, etc. All works fine. However I need to add a standalone page and I'm planning on using the ContactsController to handle the form. So I created a new view in Layouts titled widget.
The idea being that when someone fills out the contact form on the widget page there's a hidden field that handles the interest and just uses the contact mailer, etc.
However I'm running into some issues. On my view I have:
=content_for :body do
.container
.row
.col
=render partial: '/shared/widget_form'
Then in my partial:
=tb_form_for #contact, remote: true, data: {errors: :inline, success: thanks_path } do |f|
.row
.col
= tb_form_errors(f.object, :base)
.row
.col
=f.text_field :name, class: 'form-control', placeholder: 'Name', required: true
Then I updated my routes to have
resources :contacts, only: [:create]
match 'widget' -> 'contacts#create', via: :post
However I end up with
First argument in form cannot contain nil or be empty
I thought maybe it's an issue with doing post, although I want to post to the Contacts > Create, but I changed that to get and end up with:
param is missing or the value is empty: contact
So I thought I'm probably using match wrong so I'll just do:
get 'widget', to: 'contacts#create', as: :widget
Then I updated my form to have:
=tb_form_for #contact, url: widget_path, remote: true, data: {errors: :inline, success: thanks_path}
And I'm still getting
param is missing or the value is empty: contact
What am I missing? I'd just like to reuse the method on a different page.
EDIT:
If I use:
=tb_form_for Contact.new
It works....but this feels like a cheat. Any way around this?
How does rails render forms?
In short:
For example user sends a get request to an contacts#index
Inside #index action you something like #contact = Contact.new
By default index.haml view will be rendered for #index action if another view is not specified.
In this case index.haml view will be able to access #contact variable.
In your case there is method = content_for :body do that renders partial: '/shared/widget_form'
I believe there is yield :body somewhere in another view or layout.
So to make it work you need to initialize #contact variable in any controller that may render view/layout with yield :body

Can partials be controller-specific?

In Rails, can partials be controller-specific? I have a controller for creating post and a view that went with it. After I renamed the view (added _) to make it a partial, it no longer seems to work.
Thank you in advance.
Partials are not views.
Partials cannot be rendered as views. Which makes perfect sense since partials are not views - they are reusable chunks of a view.
Lets say you have:
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
def new
#post = Post.new
end
end
# app/views/posts/new.html.erb
<%= form_for(#post) do |f| %>
# ...
<% end %>
If you rename new.html.erb -> _new.html.erb Rails will no longer be able to find the view - because it no longer is a view.
Can partials be controller-specific?
Partials are by there very nature controller-specific. For example:
# app/views/posts/show.html.erb
<%= render partial: 'foo' %>
Will look for app/views/posts/_foo.html.erb.
# app/views/stories/show.html.erb
<%= render partial: 'foo' %>
Will look for app/views/stories/_foo.html.erb.
Thats because Rails prepends the view lookup path with app/views/controller_name.
Can views be shared?
Yes. But you need to explicitly tell Rails where the view is located.
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
def new
#resource = Post.new
render 'resources/new'
end
end
# app/controllers/stories_controller.rb
class StoriesController < ApplicationController
def new
#resource = Story.new
render 'resources/new'
end
end
Or you could append the view path.
class ResourcesController < ApplicationController
prepend_view_path Rails.root.join('app/views/resources')
end
class PostsController < ResourcesController
def new
#resource = Post.new
# will now default to rendering `app/views/resources/new.html.erb`
end
end
Can partials be shared?
Yes. But you need to tell Rails where the partial is located.
<%= render partial: 'shared/social_media_icons' %>

How can I modify a variable in the controller from the view (ruby on rails)

I am making a portfolio page in rails. On the front page I have an "About" section where I have a personal description of myself. I want to be able to change this dynamically (not hard-coded html).
I would like to have the description be a text variable or string that I can modify through a form in the view section.
Questions
1. How should I declare this variable in the controller?
2. How do I access and change it from the form in the view?
3. Is there a better solution to my problem?
The only way to do this is to send the updated values to your controller. You need to have a form on your portfolio page:
#config/routes.rb
resources :users do
resources :portfolios #-> url.com/users/:user_id/portfolios/:id
end
#app/controllers/portfolios_controller.rb
class PortfoliosController < ApplicationController
def show
#user = User.find params[:user_id]
end
end
#app/controllers/users_controller.rb
class UsersController < ApplicationController
def update
#user = User.find params[:id]
#user.update user_params
end
private
def user_params
params.require(:user).permit(:about)
end
end
#app/views/portfolios/show.html.erb
<%= form_for #user do |f| %>
<%= f.text_field :about %>
<%= f.submit %>
<% end %>
Without any more context, that's the best I can give.
You will need to connect a database to store and retrieve dynamic stuff. You can access a variable in views if you define it with # like;
#about= Me.last.about
where Me could be your model that the information is saved in and Me.last would be the instance of that model. You can update this information by updating the model like
Me.last.update_attributes :about=> params[:about]
where params[:about] would be the params from the field in the form.
I would recommend following a guide so you get a complete solution. Below I have the major steps a user would take to update some content. This code is not a complete solution.
Your form would submit the updated content to another controller action (through another route).
<%= form_for #user do |f| %>
In that controller, you would store the content (usually in the database).
def update
#user = User.find(params[:id])
#user.update(user_params)
redirect_to :back
end
The original controller action would have a variable that got the content from the place you stored it
def show
#user = User.find(params[:id])
end

Rails 4 before_action for HTTP Post

I have a before action on a create method like so:
class ApplicationController
before_action :authenticate_user!
end
class PostsController < ApplicationController
def create
#post = Post.new(post_params)
if #post.save
redirect_to post_path(#post)
else
redirect_to root_path
end
end
end
#on some view while logged out, a user may see a form like so
<%= form_for :post, url: posts_path, method: :post do |f| %>
# some post data
<%= f.submit %>
<% end %>
I want to be able to be a logged out user and view a form that allows the guest to post. When the guest clicks the form submit button, I would like it to render the login form and after successful login to proceed to the create method.
Everything is working up until the point of proceeding to the posts#create method. Instead, it is rendering root_path. I am unsure if this is part of the desired functionality of before_action or not.
So, is it possible to have a before_action continue to a HTTP Post method with the original params from the form?
If you are using devise, you could add something like this to your ApplicationController
def after_sign_in_path_for(resource)
redirect_to request.referrer
end
This will ensure that every time after signin, you refer to the previous page.

Could find Addict without an ID

I'm trying to put a new form that creates new "Addicts" in a modal in my home page.
It's a simple form with 2 inputs, that when clicking on New, a modal pops up with that form in my index page.
I can't get it to work because it keeps saying "Couldnt find Addict without an ID".
My Pages Controller
class PagesController < ApplicationController
def home
#addict = Addict.find(params[:id])
#lanzaderas = Lanzadera.all
render 'index'
end
end
My Addict Controller
class AddictsController < ApplicationController
def index
#posts = Addict.all
end
def show
#addict = Addict.find(params[:id])
end
def new
#addict = Addict.new(params[:addict])
end
def create
#addict = Addict.new(params[:addict])
if #addict.save
redirect_to posts_path, :notice => "Your Addict was saved"
else
render "new"
end
end
def edit
end
def update
end
def destroy
end
end
end
My form in my modal
<%= form_for #addict do |f| %>
<%= f.input :name %>
<%= f.input :surname %>
<%= f.input :postal %>
<%= f.submit %>
<% end %>
I know it has something to do with the variable / id not being passed correctly in my Controller, but it's an error I get lots of times and don't know why I happens.
Thanks!
In def home in your PagesController you have this code:
#addict = Addict.find(params[:id])
I suspect, that you don't have the id for 'addict' in your parameters, when you visit your home action.
Do you want to display one particular addict in your 'home' page? If not, you can remove this line.
Update:
Change this in your AddictsController:
def new
#addict = Addict.new
end
In the new action you only "prepare" a new addict object. Using the find method is not possible, since the record hasn't been created yet.
If you're using Rails 4 you also have to permit your parameters (for security reasons; more info here: Railsguides: Strong Parameters)
In your case you have to do 2 things:
First: add this at the bottom of your AddictsController:
private
def addict_params
params.require(:addict).permit(:name, :surname, :postal)
end
Second: use this method in your create action instead of params[:addict]:
def create
#addict = Addict.new(addict_params)
if #addict.save
redirect_to posts_path, :notice => "Your Addict was saved"
else
render "new"
end
end

Resources