Im new to ruby on rails so i need some tips please.
Im trying to render some checkboxes on the edit view for a user.
I have tried to follow the documentation for the nested_attributes but the checkboes does not render.
Here is the relation between the two models:
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :softwares
has_many :courses
accepts_nested_attributes_for :softwares
accepts_nested_attributes_for :courses
The Edit view for a user
<div class="container">
<div class="row">
<div class="col-md-12 mt-5">
<%= form_for #student, url: auth_student_path(#student), method: :put do |f| %>
<div class="col-md-12 mb-5 mt-5">
<div class="row">
<h3 class="mb-3 filter_heading">Softwares</h3>
<% #softwares.each do |sf|%>
<%= f.fields_for :softwares do |software| %>
<div class="col-md-3">
<div class="courses">
<%= software.label sf.title %>
<%= software.check_box :title, {multiple: true}, sf.title, nil %>
</div>
<% sf.courses.each do |crs|%>
<%= f.fields_for :courses do |course|%>
<div class="mt-1 courses-checkbox">
<%= course.label crs.name %>
<%= course.check_box :name, {multiple: true}, crs.name , nil %>
</div>
<% end %>
<% end%>
</div>
<% end %>
<% end%>
</div>
<div class="form-group">
<%= f.submit "Save", class:"btn btn-primary"%>
</div>
<% end %>
</div>
</div>
</div>
The Controller
module Auth
class StudentsController < ApplicationController
before_action :authenticate_user!
before_action :set_student, only: %i[delete_certificates]
def edit
authorize! :edit, #user
#softwares = Software.all
#student = User.find(params[:id])
end
def update
authorize! :update, #user
#student = User.find(params[:id])
if #student.update(student_params)
redirect_to edit_auth_student_path(#student)
else
redirect_to edit_auth_student_path(#student)
end
end
def show
def set_student
#student = User.find(params[:student_id])
end
private
def student_params
params.require(:user).permit(
:email,
:firstname,
:lastname,
:phone,
:locked,
:approved,
:role,
badges: [],
certificates: [],
softwares_attributes: [:title],
courses_attributes: [:name],
)
end
end
end
Please help me.
You don't need accepts_nested_attributes_for just to select existing records and associate them with something. Its only needed if you need to create/update the other record (the course or software) at the same time.
I'm also guessing you don't actually want to have a one-to-many assocation and duplicate every course and every software for each user - instead you want a many to many assocation and some data normalization.
So create a join table to hold the assocation between users and courses for example:
class User < ApplicationRecord
has_many :enrollments, foreign_key: :student_id
has_many :courses, through: :enrollments
end
# rails g model enrollment student:belongs_to course:belongs_to
class Enrollment < ApplicationRecord
belongs_to :student, class_name: 'User'
belongs_to :course
end
class Course < ApplicationRecord
has_many :enrollments
has_many :students, through: :enrollments
end
And then you just create inputs that use the course_ids / course_ids= setter and getter created by has_many :courses, through: :enrollments.
<%= form_with(model: #student, url: auth_student_path(#student), method: :put) do |f| %>
<div class="field">
<%= f.label :course_ids, 'Select your courses' %>
<%= f.collection_select :course_ids, #courses, :id, :name, multiple: true %>
</div>
# ...
<% end %>
And then you just whitelist an array of ids in your controller:
params.require(:user)
.permit(
# ...
course_ids: []
)
In fact if your ever passing existing records as anything but an ID you're doing it very wrong.
There are still plenty of issues with this code but this should at least be nudge in the correct direction.
Related
I am using cocoon in my Rails app for assigning employees (users) to projects (many to many connection). The creation of associations is working correctly, but each time I add another employee cocoon adds an empty form field in the edit view. None of the other cocoon form fields in the edit view are populated either. Could this be due to the usage of dropdowns (select)?
When I inspect the form in my browser I can see that each field seems to be assigned to one of the associations, but the selection is still empty.
What I would like to achieve is, that every association is displayed in a cocoon form field, so that they can be edited. Thanks for any help in advance!
My code is below (Sorry for any mess, it is my first time trying out a many to many connection of two models).
Project Edit View
<%= form_for(#project, :url => project_path, method: :patch) do |f| %>
<div class="form-group">
<%= f.label :title %>
<%= f.text_field :title, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :customer %>
<%= f.text_field :customer, class: "form-control" %>
</div>
<%= f.fields_for :user_projects do |collab| %>
<% collab.hidden_field :project_id, value: #project.id %>
<%= render 'user_project_fields', f: collab %>
<% end %>
<div class="add-collaborator">
<%= link_to_add_association "add", f, :user_projects, class: "btn btn-mmc" %>
</div>
<div class="actions">
<%= f.submit "Save Changes", class: "btn btn-mmc btn-mmc-medium" %>
</div>
<% end %>
cocoon field partial
<div class="nested-fields">
<%= f.label "Select User" %>
<div class="form-group custom-form-group">
<%= f.select(:user_id, options_for_select(User.all.map { |u| [u.email, u.id] }), {include_blank: true}, {class: 'form-control'})%>
<div class="btn-user-project">
<%= link_to_remove_association "x", f, class: "btn btn-mmc-attention btn-mmc" %>
</div>
</div>
</div>
Project Model
class Project < ApplicationRecord
has_many :user_projects
has_many :users, :through => :user_projects
accepts_nested_attributes_for :user_projects, reject_if: :all_blank, allow_destroy: true
end
User Model
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :user_projects
has_many :projects, :through => :user_projects
end
Project Controller
def edit
#project = Project.find(params[:id])
#project.user_projects.build
end
def update
#project = Project.find(params[:id])
#project.update(project_params)
#new_collaborator = UserProject.new(user_id: params[:user_id], project_id: params[:project_id])
#new_collaborator.save
if #project.update(project_params) && #new_collaborator.save
redirect_to projects_path
else
render :edit
end
end
private
def project_params
params.require(:project).permit(:title, :customer, :delted, user_projects_attributes: [:user_id, :project_id]).reject { |_, v| v.blank? }
end
I am guessing the mapping to the actual value is not done correctly, e.g. the value of the user_id is not marked as selected, in the options_for_select you have to add the selected value as parameter (see documentation).
However, there is a much easier version:
<%= f.collection_select(:user_id, User.all, :id, :email) %>
BTW using a gem like simple_form also makes building forms a lot more intuitive and straightforward.
That's because you're create object twice.
First time:
#project.update(project_params) # from accepts_nested_attributes_for
Second time:
#new_collaborator = UserProject.new(user_id: params[:user_id], project_id: params[:project_id])
#new_collaborator.save
P.s.
Can you show project_params method? I think I know why first object empty
I am unsure why my cocoon nested form is not appearing and any help would be much appreciated
the <button><%= link_to_add_association 'add a required skill', f, :requiredskills %></button> works perfectly well in displaying a form
but i am unsure why this is not rendering the form <%= render 'requiredskill_fields', :f => duty %>
what i would like to do is to have a form already displayed and when a user clicks add a required skill another form is displayed.
At present when a user clicks add a required skill that displays
another form - the main issue is having a form to be displayed
initially. I am assuming this <%= render 'requiredskill_fields', :f
=> duty %> is suppose to put a form in place but i am unsure why it is not working
user/_form.html.erb
<%= simple_form_for(#user) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :firstname %>
<%= f.input :lastname %>
<h3>required skills & expertise</h3>
<div>
<div class="requiredskill_info" id="skill">
<%= f.simple_fields_for :requiredskills do |skill| %>
<%= render 'requiredskill_fields', :f => skill %>
<% end %>
<div class="add_requiredskill"><button><%= link_to_add_association 'add a required skill', f, :requiredskills %></button></div>
</div>
</div>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
user/_requiredskill_fields.html.erb
<div class="nested-fields">
<%= f.association :category_advert, collection: CategoryAdvert.all, prompt: "select a category" %>
<%= f.grouped_collection_select :category_advertskill_id, CategoryAdvert.order(:name), :category_advertskills, :name, :id, :name, {prompt: "Select a category"}, {class: "category_advertskill"} %>
<button><%= link_to_remove_association 'remove required skill', f %></button>
</div>
users_controller.rb
class UsersController < ApplicationController
respond_to :html, :xml, :json
before_action :set_user, only: [:show, :edit, :update, :destroy]
def index
#users = User.all
#user = current_user
end
def show
#resume = #user.resume
end
def edit
end
def update
#user.update(user_params)
redirect_to #user
end
private
def set_user
#user = User.find(params[:id])
end
def user_params
params.require(:user).permit(:email, :firstname, :lastname, :city, :category_qualification_id, :category_careerlevel_id, :desiredjob, :category_distance_id, :preferedlocation, :category_notice_id, :category_country_id, :category_positiontype_id, :image, :cvattachment, :hidecv, :jobadvice, :validlicense, :owntransport, :considerrelocation, :preferredlocation, :neednotice, :stratdate, :availabletowork, :category_outsource_id, :category_advertskill_id, :category_age_id, languages_attributes: [:id, :name, :_destroy], requiredskills_attributes: [:id, :name, :category_advert_id, :category_advertskill_id, :category_year_id, :category_level_id, :_destroy])
end
end
user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :requiredskills
has_many :category_advertskills, through: :requiredskills
has_many :category_adverts, through: :requiredskills
has_one :resume
accepts_nested_attributes_for :languages, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :requiredskills, :reject_if => :all_blank, :allow_destroy => true
end
def edit
#requiredskills = #user.requiredskills.present? ? #user.requiredskills : #user.requiredskills.build
end
change this code in controller
In my rails project, a user can only rate his teacher if he already signed up and logged in. However, after the user logged in and rated the teacher, I couldn't see his rating. I don't know what cause the problem. Any suggestions would be appreciated.
Here is my ratings_controller.rb:
class RatingsController < ApplicationController
before_action :authenticate_user!
before_action :get_teacher
def new
get_teacher
#rating = current_user.ratings.build
end
def create
get_teacher
#rating = current_user.ratings.create(rating_params)
if #rating.save
redirect_to school_teacher_path(#teacher.school, #teacher)
else
render 'new'
end
end
def destroy
get_teacher
#rating = #teacher.ratings.find(params[:id])
#rating.destroy
redirect_to school_teacher_path(#teacher.school, #teacher)
end
def get_teacher
#teacher = Teacher.find(params[:teacher_id])
end
private
def rating_params
params.require(:rating).permit(:easiness, :helpfulness, :clarity, :comment,
:teacher_id, :school_id)
end
end
teachers/show.html.erb:
<!-- Caculate the average rating of the teacher -->
<h1>Average ratings:</h1>
<p>Clarity:
<%= #teacher.ratings.average(:clarity) %>
</p>
<p>Easiness:
<%= #teacher.ratings.average(:easiness) %>
</p>
<p>Helpfulness:
<%= #teacher.ratings.average(:helpfulness) %>
</p>
<hr>
<!-- Show all the ratings -->
<h2>All the ratings:</h2>
<div>
<%= #teacher.ratings.each do |rating| %>
<p>Clarity:
<%= rating.clarity %>
</p>
<p>Helpfulness:
<%= rating.helpfulness %>
</p>
<p>Easiness:
<%= rating.easiness %>
</p>
<p>Comment:
<%= rating.comment %>
</p>
<%= link_to "Delete rating", [rating.teacher, rating], method: :delete, data: { confirm: 'Are you sure?' }, class: "btn btn-warning" %>
<hr>
<% end %>
</div>
<p>
<%= link_to "Rate teacher", new_teacher_rating_path(#teacher), class: "btn btn-primary" %>
</p>
<p>
<%= link_to "Back to school", school_path(#school), class: "btn btn-primary" %>
</p>
ratings/new.html.erb:
<h1>Teacher Rating</h1>
<%= form_for([#teacher, #rating]) do |f| %>
<p>
<%= f.label :clarity %>
<%= f.text_field :clarity %>
</p>
<p>
<%= f.label :easiness %>
<%= f.text_field :easiness %>
</p>
<p>
<%= f.label :helpfulness %>
<%= f.text_field :helpfulness %>
</p>
<p>
<%= f.label :comment %>
<br>
<%= f.text_area :comment %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
rating.rb:
class Rating < ActiveRecord::Base
belongs_to :teacher
belongs_to :user
end
user.rb:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :ratings
has_many :teachers
end
teacher.rb:
class Teacher < ActiveRecord::Base
belongs_to :school
has_many :ratings, dependent: :destroy
has_many :users
def name
"#{firstName} #{middleName} #{lastName}"
end
def to_s
name
end
end
I think your problem is rating, not associated with user. You can use build method, it will add user_id to rating automatically,
#rating = current_user.ratings.build(rating_params)
and you not associating teacher id,
#rating.teacher_id = #teacher.id
And your the relation between user and teacher, both has has_many it's incorrect syntax, you should use has_and_belongs_to_many. I'm not sure about your database schema, If you have ratings as relation in between user and teachers you can use 'has_many through:', as suggested below
user.rb:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :ratings
has_many :teachers, through: :ratings
end
teacher.rb:
class Teacher < ActiveRecord::Base
belongs_to :school
has_many :ratings, dependent: :destroy
has_many :users, through: :ratings
def name
"#{firstName} #{middleName} #{lastName}"
end
def to_s
name
end
end
I'm trying to implement a dual registration (users can be either customers or providers) with Devise, but I'm getting nowhere (>_<).
So I have a sign up link with a parameter http://localhost:3000/users/sign_up?type=customer and http://localhost:3000/users/sign_up?type=provider
My problem is that if I put the nested form with :provider like <%= f.fields_for :provider do |fp| %> as I expect it to be, because is an has_one association it isn't shown. And if I put it with :providers like <%= f.fields_for :providers do |fp| %> the field is properly shown in the form, but it is not saved.
I tried some of the proposed things in other posts (like this and this), but nothing seems to work for me...
Here is a simplified version of my code:
Routes:
Rails.application.routes.draw do
devise_for :users, :controllers => { registrations: 'registrations' }
resources :users
resources :customers
resources :providers
end
Models:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :customer
accepts_nested_attributes_for :customer
has_one :provider
accepts_nested_attributes_for :provider
end
class Customer < ActiveRecord::Base
belongs_to :user
end
class Provider < ActiveRecord::Base
belongs_to :user
end
Controllers:
class RegistrationsController < Devise::RegistrationsController
def sign_up_params
params.require(resource_name).permit(:email, :password, :password_confirmation, customer: [:field_x], :provider: [:field_y]))
end
end
Views:
<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
...
<% if params[:type] == "customer" %>
<%= f.fields_for :customers do |fc| %>
<div class="field">
<%= fc.label :field_x %><br />
<%= fc.text_field :field_x, autofocus: true %>
</div>
<% end %>
<% end %>
<% if params[:type] == "provider" %>
<%= f.fields_for :providers do |fp| %>
<div class="field">
<%= fp.label :field_y %><br />
<%= fp.text_field :field_y, autofocus: true %>
</div>
<% end %>
<% end %>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<% end %>
Thanks a lot!
When you are using fields_for :provider result is not displaying because you have not build it, build the association for nested attributes.
When you are using fields_for :providers record are not being save because in sign_up_params method you are not white listed providers attribute.
Also in strong parameter if you are using association(nested attribute) then you should use provider_attributes instead of provider.
I am working on my first rails application and I am attempting to add a comment section. The comments will fall under each topic. I am seeing comments that are being generated but I am not able to get it configured so that I can input my own comments. I will list below the changes I have made to my code as instructed by my assignment.
Heres a change to my app/models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :user
validates :body, length: { minimum: 5 }
validates :body, presence: true
validates :user_id, presence: true
end
Heres my app/models/user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
has_many :posts
has_many :comments
mount_uploader :avatar, AvatarUploader
def admin?
role == 'admin'
end
def moderator?
role == 'moderator'
end
end
I have to also update db/seeds.rb
# Create Comments
100.times do
Comment.create!(
# user: users.sample, # we have not yet associated Users with Comments
post: posts.sample,
user: users.sample,
body: Faker::Lorem.paragraph
)
end
Updated my config/routes.rb
resources :comments, only: [:create]
Updated my app/controllers/comments_controller.rb
class CommentsController < ApplicationController
def create
#post = Post.find(params[:post_id])
#comment = current_user.comments.new(comment_params)
#comment.post = #post
#new_comment = Comment.new
authorize #comment
end
end
The final adjustments I made was to app/views/comments/_form.html.erb
<% if current_user %>
<h4>Add a comment:</h4>
<%= form_for [#post, #post.comments.build], remote: true do |f| %>
<%= f.label :body %>
<%= f.text_field :body %>
<%= f.submit %>
<% end %>
<% end %>
I hope that this is sufficient enough for some type of a response as to what I may be missing. I do not get an error, the box that I am looking for to create a comment just does not appear. Here is an example of what I am trying to accomplish:
http://oi62.tinypic.com/f35h5y.jpg
edit:
Here is the app/views/posts/show.html.erb
<h1><%= markdown #post.title %></h1>
<div class="row">
<div class="col-md-8">
<small>
<%= image_tag(#post.user.avatar.tiny.url) if #post.user.avatar? %>
submitted <%= time_ago_in_words(#post.created_at) %> ago by
<%= #post.user.name %>
</small>
<p><%= markdown #post.body %></p>
<p><%= image_tag(#post.image_url) if #post.image? %></p>
</div>
<div class="col-md-4">
<% if policy(#post).edit? %>
<%= link_to "Edit", edit_topic_post_path(#topic, #post), class: 'btn btn-success' %>
<% end %>
</div>
</div>