Rails submit button for boolean attribbute - ruby-on-rails

I wanted to know how to go about have a submit button that will change a boolean attribute, instead of using radio buttons.
As a example if I display a list of 'published' and unpublished' article posts on the Post#index page and I wanted a button called 'Publish' that sets the :is_published field on the Post model to true.
I'm using strong_parameters on Rails 3.2.13
I was thinking in the Post controller I would have
def index
#post = Post.recent
end
def update
#post = Post.find_by_slug(params[:id])
if params[:publish_button]
#post.is_published = true
#post.save
redirect_to root_path
end
end
private
def post_params
params.require(:person).permit(:body, :publish_button)
end
In my Post#index view I have a form_for that has <%= f.submit 'Publish', name: publish_button %>
Here is the view in Post#index
<%= form_for #post do |f| %>
<div class="field">
<%= f.label :body %><br />
<%= f.text_field :body %>
</div>
<div class="actions">
<%= f.submit %>
<%= f.submit_tag 'Publish Post', name: 'publish_post' %>
</div>
<% end %>
The simple model follows
class Post < ActiveRecord::Base
include ActiveModel::ForbiddenAttributesProtection
scope :recent, -> { order('updated_at DESC') }
end
But I'm getting an error of Required parameter missing: post
Thanks in advance.
Update
I've added a model and view that corresponds to the question. I hope it helps.

pass in your values as hidden fields, e.g.
= form_for #post do |f|
= f.hidden_field :is_published, value: "1"
= f.submit "Publish"
and if you want it to be inline like a button_to, give it the button_to class:
= form_for current_user.profile, html: { class: 'button_to' } do |f|
...

Related

Nested Attributes Child model don't save

I'm having a problem in the model saving with nested attributes.
In the app, there's a Customer, that have 1..n Contacts witch in turn have 1..n Telephones.
I've searched a lot before asking here, and decided to make it save only the Contact first. Well, at first the Customer is stored, but Contact is not. From what I read there's no need to repeat the ... contacts.build from new function in the create, and that the line "#customer = Customer.new(customer_params)" would create and store them both.
Why it's not working? (That's the first question.)
After some modifications and debugging, I found that when I set a second line building Contact (...contacts.build(customer_params[:contacts_attributes])) it's not saved because of an error of 'unknown attribute'. That's because between the hash :contacts_attribute and the content of it, it's added another hash, called ':0' (?). The structure of the hash that comes from the form is this :
":contacts_attribute[:0[:name, :department, :email]]"
I imagine that this hash :0 is for adding more than one Contact instance, that will come in hashes :1, :2 etc.
There's a way to store the Contact instance by getting this :0 hash? (How do I access this hash? Is it "... :contacts_attribute[0]"?)
Below is the relevant code.
Thanks for the attention!
customer.rb
class Customer < ActiveRecord::Base
...
has_many :contacts
accepts_nested_attributes_for :contacts, reject_if: lambda {|attributes| attributes['kind'].blank?}
...
def change_by(user_id)
update_attributes(changed_by: user_id, deleted_at: Time.now, updated_at: Time.now)
end
def delete(user_id)
update_attributes(status: false, changed_by: user_id, deleted_at: Time.now, updated_at: Time.now)
end
private
...
end
customers_controller.rb
class CustomersController < ApplicationController
def new
#customer = Customer.new
#customer.contacts.new
end
def create
user_id = session[:user_id]
#customer = Customer.new(customer_params)
if #customer.save
#customer.change_by(user_id)
flash[:success] = "Cliente cadastrado com sucesso!"
redirect_to customers_url
else
render 'new'
end
end
private
def customer_params
params.require(:customer).permit(:razao_social, :nome, :CPF_CNPJ,
:adress_id, :email_nota, :transporter_id, :observacao,
contacts_attributes: [:nome, :setor, :email])
end
Form
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for #customer do |f| %>
<%= f.label "Dados Básicos" %>
<div class="well">
<%= f.label :razao_social, "Razão Social" %>
<%= f.text_field :razao_social %>
<%= f.label :nome, "Nome" %>
<%= f.text_field :nome %>
<%= f.label :CPF_CNPJ, "CPF/CNPJ" %>
<%= f.text_field :CPF_CNPJ %>
<%= f.label :email_nota, "Email para nota" %>
<%= f.email_field :email_nota %>
<%= f.label :observacao, "Observações" %>
<%= f.text_area :observacao %>
</div>
<%= f.fields_for :contacts do |k| %>
<%= k.label "Contato" %>
<div class="well">
<%= k.label :nome, "Nome" %>
<%= k.text_field :nome %>
<%= k.label :setor, "Setor" %>
<%= k.text_field :setor %>
<%= k.label :email, "Email" %>
<%= k.email_field :email %>
</div>
<% end %>
<%= f.submit "Cadastrar Cliente", class: "btn btn-primary" %>
<% end %>
</div>
reject_if: lambda {|attributes| attributes['kind'].blank?}
No sign of :kind in your form or your customer_params
This might have something to do with it.
Other than that, if you need an add/remove relationship for contacts, check out the cocoon gem. If you only need one, then build that into your fields for:
<%= f.fields_for :contacts, #customer.contacts.first || #customer.contacts.build do |k| %>
The form will then be specific to a single instance of contact.
There's a way to store the Contact instance by getting this :0 hash?
(How do I access this hash? Is it "... :contacts_attribute[0]"?)
You don't need to access it, that's what the accepts_nested_attributes is for. The rest of your code looks ok so sort out the rejection issue at the top and come back if there are still problems, and post the log output - specifically the params hash for the request!

How to display data from one single column into multiple inputs?

I work on an application and I have a model with a 'movies' attribute (top 5 movies). When a user tries to edit the top 5 movies I want to display each movie in a different input, but I don't know how to do it (because all 5 movies represent one single attribute)
In my controller
def show
#user = User.find(params[:id])
#user.movies = #user.movies.split(',')
end
In my view
<%= form_for #user do |f| %>
<%= f.label :movies %>
<%= f.text_field :movies[0] %>
.
.
.
<%= f.label :movies %>
<%= f.text_field :movies[4] %>
<% end %>
Of course, this doesn't work!
You can add use a virtual attribute in your User model to handle this, like this:
class User < ActiveRecord::Base
def movies_list
movies.split(',')
end
def movies_list=(list)
self.movies = list.join(',')
end
end
Then in your controller you just do:
def show
#user = User.find(params[:id])
end
And in your view you can handle your form like this:
<%= form_for #user do |f| %>
<%= f.label :movies %>
<%- #user.movies_list.each do |movie| %>
<%= text_field_tag 'user[movies_list][]' %>
<% end %>
<% end %>
That will allow your update action on the users_controller to access the movies array in params[:user][:movies_list], that the model will understand and process.
At last, don't forget to add :movies_list to your permitted params on the controller if you need to.
EDIT: Thanks to #BroiSatse in the comments, as an alternative to the text_field_tag syntax you can use form-builder's f.text_field :movies_list, multiple: true, value: movie

I'm getting a NoMethodError on rails, unsure how to access a model through another model

Im writing a game on rails, and am trying to allow a user to create their mine (its a mining game).
I have a table for the users, and a table for mines.
Each user has a ref. ID on their entry, pointing to their mine's ID in the mine table.
I'm getting an error when I try to visit /users/1/mines/new.
undefined method `mines_path'
I can't figure out why.
form in New:
<%= form_for [#mine] do |f| %>
<%= f.label :name %>
<%= f.text_field :name %><br>
<p>Depth: <%= #mine.depth %></p>
<%= f.submit "Submit", id: "submit" %>
<% end %>
Controller:
def new
#user = User.find(params[:user_id])
#mine = #user.mines.new
end
def create
#mine = #user.mines.create(mine_params)
if #mine.save
redirect_to users_mines_path
else
render new_mines_path
end
end
routes:
root 'welcome#index'
resources :sessions, only: [:create]
resources :users do
resources :mines
end
resources :tools, only: [:create]
How can I create a new mine THROUGH the user? Am I doing this correctly in my controller?
Thanks!
In your routes you have mines nested inside users so you need to change your form to something like this:
<%= form_for [#user,#mine] do |f| %>
<%= f.label :name %>
<%= f.text_field :name %><br>
<p>Depth: <%= #mine.depth %></p>
<%= f.submit "Submit", id: "submit" %>
<% end %>
OR
You can specify url option with your path:
<%= form_for #mine, url: user_mines_path(#user) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %><br>
<p>Depth: <%= #mine.depth %></p>
<%= f.submit "Submit", id: "submit" %>
<% end %>
For details on forms refer to Form Helpers
Also as #Vimsha pointed out in your controller you need to use .new instead of .create as create will initialize and save your your mine.
def create
#mine = #user.mines.new(mine_params)
if #mine.save
redirect_to user_mines_path
else
render new_user_mine_path
end
end
#user.mines.create will create the mine. So use #user.mines.new
named route for mine index will be user_mines_path
named route for mine show will be user_mine_path(#mine)
named route for new mine will be new_user_mine_path
Contoller:
def create
#mine = #user.mines.new(mine_params)
if #mine.save
redirect_to user_mines_path
else
render new_user_mine_path
end
end

Form resulting in blank post with no ID

I am new to Rails and working on creating a generic "facebook" type of app as practice with users and posts associated with each user. However, I'm currently having an issue where I think the form that I am using to create the posts is also being rendered out as a blank post with no post ID where I display all of the posts in a section below. I think that this post is being shown even before it is being saved to the database.
Here is my code in my view:
<div class="newpostcontainer">
<div class="newposttext">
<%= form_for([#user, #user.posts.build]) do |f| %>
<%= f.text_area :post, size: "69x1" %>
</div>
<div class="newpostsubmitbutton">
<%= f.submit %>
</div>
<% end %>
</div>
<% #user.posts.reverse_each do |p| %>
<div class="postedcontainer">
<div class="minipostpic">
<%= image_tag #user.photo.url, width: 32, height: 32 %>
</div>
<div class="nameofposter"><%= #user.name %></div>
<div class="dateofpost"><%= p.created_at%></div>
<div class="postcontent"><%= p.id%></div> <br>
<div class="postcontent"><%= p.post%></div> <br>
<div class="likecommentdelete">
<%= link_to "Delete", [p.user, p], method: :delete %> | Like | Comment
</div>
</div>
<%end%>
</div>
Here is my controller:
def index
#user = User.find(params[:user_id])
#posts = #user.posts.all
end
def create
#user = User.find(params[:user_id])
#post = #user.posts.create!(post_params)
redirect_to user_path(#user)
end
def show
#user = User.find(params[:user_id])
#post = #user.posts.find(params[:id])
redirect_to user_path(#user)
end
def destroy
#user = User.find(params[:user_id])
#post = #user.posts.find(params[:id])
#post.destroy
if #post.destroy
redirect_to user_path(#user)
else
redirect_to users_path
end
end
private
def post_params
params.require(:post).permit!
end
end
And here is my model:
class Post < ActiveRecord::Base
belongs_to :user
has_many :comments
validates_presence_of :post
end
I'm pretty sure the issue has something to do with my form to create the new post because when I remove it or comment it out, the extra blank post with no post ID goes away.
Any thoughts or suggestions?
Thank you!!
I think you need to permit the field values to be posted:
i.e.,
params.require(:post).permit!
should be
params.require(:post).permit(:name, :post)
then only it will POST I think.
Hope it helps :)
This is because of rails 4 strong parameter feature. You need to whitelist your active models parameters. For more details refer to here.
In your case you need to do something like this:
params.require(:post).permit(:post)
where the ":post" inside require is your model and the other one is your permitted field that is your textarea.
Several issues -
Form
<%= form_for([#user, #user.posts.build]) do |f| %>
Why are you building an associative object? #user.posts.build will not persist your data, and will cause all sorts of non-conventional issues I would highly recommending building the posts associative object in your controller's new action before using in the view, so you can do this:
#app/controllers/users_controller.rb
def new
#user = current_user
#user.posts.build
end
<%= form_for #user do |f| %>
Association
You're trying to edit the post attribute with this statement:
<%= f.text_area :post, size: "69x1" %>
This won't work in any circumstance, as :post is an association, not an object. Rails only allows you to change / add attributes to specific objects, which means you'll be better doing something like this:
<%= f.fields_for :posts do |p| %>
<%= p.text_area :title %>
<%= p.text_area :body %>
<% end %>
Strong Params
You're currently permitting all your params? You'll be better doing this:
def post_params
params.require(:user).permit(posts_attributes: [:title, :body])
end
Use Posts Controller
A better way will be to just use the posts_controller, like this:
#app/controllers/posts_controller.rb
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
#post.save
end
#app/views/posts/new.html.erb
<%= form_for #post do |f| %>
<%= f.text_field :title %>
<%= f.text_field :body %>
<% end %>

Set a value on form submit if checkbox is checked in Rails?

I have a form that creates a Post. A Post has a non-required field called school_id. On the Post form (to create a new Post) I have a checkbox. If that checkbox is checked, I want to set :school_id to equal the school_id that's also set to current_user (object created by Devise). How can I set the Post.school_id to equal current_user.school_id if the checkbox is checked?
The checkbox I have on the form is passing a :school_id of 1 and never changes. Is this because checkboxes can only accept boolean values of either 1 or 0? Here's what I have on the form so far:
<%= simple_form_for #post do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.text_area :content, required: true %>
<% if #school %>
<%= f.label :school_id, "Set school",:class => "checkbox inline" %>
<%= f.check_box :school_id, :value => current_user.school.id %>
<% end %>
<%= f.submit "Submit Post", class: 'btn btn-primary' %>
<% end %>
EDIT
post controller
def create
#school = current_user.school
#post = current_user.posts.build(params[:post])
#post.school_id = current_user.school_id if #school && #post.use_school.present?
end
controller with the post form
def index
#post = current_user.posts.build
#school = current_user.school
#post.school_id = current_user.school_id if #school && #post.use_school.present?
respond_to do |format|
format.html # index.html.erb
format.json
format.js
end
end
Your problem comes from Simpleform! Simpleform forces checkbox input to be a boolean. You'll have the same problem with other syntaxes:
<%= f.input :school_id, :as => :boolean, :input_html => { :value => current_user.school.id } %>
To go deeper:
<%= f.check_box :school_id, :value => current_user.school.id %>
will generate something like this:
<input type="hidden" name="post[school_id]" value="0">
<input type="checkbox" name="post[school_id]" value="1">
Note: Simpleform automatically adds the first line (a good practice) to be sure that a value (0) is sent on submit when the input is unchecked. Otherwise, you may have issues on model update.
Your field is not a boolean, you shouldn't use a checkbox. Moreover, an user can edit the value of the checkbox (firebug & co) and that may lead to inconsistent datas or hack. So, with a checkbox you should check the school_id is correct in your controller.
I suggest this workarround:
app/views/posts/new.rb:
<% if #school %>
<%= f.label :use_school, "Set school",:class => "checkbox inline" %>
<%= f.input :use_school, :as => :boolean %>
<% end %>
app/models/post.rb:
attr_accessor :use_school
app/controllers/posts_controller.rb:
#post = Post.new(params[:post])
#post.school_id = current_user.school_id if #school && #post.use_school.present?
Note: the controller part may be done directly in your model with a :before_save.

Resources