Ruby on Rails: updating model when checkbox is changed - ruby-on-rails

I got projects and todos inside them, todos have checkbox & text, so when I change the checkbox the whole model must be updated without any buttons.
Here is my project on heroku: https://polar-scrubland-30279.herokuapp.com/
index.html.erb ( I know that I'm not the best code speller)
<% #projects.each do |project| %>
<div class="col-md-4">
<div class="project">
<%= form_for(local_assigns[:project] || project) do |f| %>
<div class="project-header">
<h2> <%= f.object.title %> </h2>
</div>
<div class="project-todos">
<%= f.fields_for(:todos) do |tf| %>
<div class="todo">
<div class="todo--checkbox">
<%= tf.check_box :isCompleted %>
</div>
<div class="todo--text">
<%= tf.object.text %>
</div>
</div>
<% end %>
</div>
<% end %>
</div>
</div>
<% end %>
This is my ProjectsController:
class ProjectsController < ApplicationController
def index
#projects = Project.all
end
def show
#project = Project.find(params[:id])
end
def new
#project = Project.new
end
def create
#project = Project.new(project_params)
if #project.save
# ...
else
# ...
end
end
def update
if #project.update(project_params)
# ...
else
# ...
end
end
private
def set_project
#project = Project.find(params[:id])
end
def project_params
params.require(:project).permit(:foo, :bar, todos_attributes: [:isCompleted, :text])
end
end
project.rb
class Project < ActiveRecord::Base
has_many :todos
accepts_nested_attributes_for :todos
end
todo.rb
class Todo < ActiveRecord::Base
belongs_to :project
end
The question is: how do I connect my form with my controller, and change todo's boolean variable when checkbox is checked/uncheked. I've tried some variants, but there is need a refresh button, which I don't wanna have.
GitHub - https://github.com/NanoBreaker/taskmanager

You have to use
jQuery checkbox change and click event
to catch checkbox change event,
further you can use ajax request
http://api.jquery.com/jquery.ajax/ on the change event,
in the ajax request use path to update method in Projects controller.
Note that you have to put the path in file config/routes.rb,
something like this:
post 'url' => 'projects#update'

Related

Rails Error: ActiveRecord::RecordNotFound

I am working on my first Rails project and I am running into a persistent issue. I suspect it has something to do with the routing, however, I can't seem to find anything about it online.
I assume it a rather simple fix, so please take a look and let me know if you can help.
TL;DR
What I was trying to achieve
Account detail Cards display Name, Phone number, and a note.
A delete and edit button would allow users to delete or edit.
What is happening:
Edit and Delete buttons return a weird param.
see image
Image of error, Showing Rails getting a different ID
Controller
class AccountdetailsController < ApplicationController
def index
#accountdetail = Accountdetail.all
end
#I can't find the ID to show the relevent card.
def show
#accountdetail = Accountdetail.find(params[:id])
if #accountdetail.nil?
redirect_to accountdetail_path
end
end
def new
#accountdetail = Accountdetail.new
end
def edit
#accountdetail = Accountdetail.find(params[:id])
end
def create
#accountdetail = Accountdetail.new(accountdetail_params)
if #accountdetail.save
redirect_to #accountdetail
else
render 'new'
end
end
#it affects this
def update
#accountdetail = Accountdetail.find(params[:id])
if #accountdetail.update(accountdetail_params)
redirect_to accountdetail
else
render 'edit'
end
end
#and this
def destroy
#accountdetail = Accountdetail.find(params[:id])
#accountdetail.destroy
redirect_to accountdetail_path
end
private def accountdetail_params
params.require(:accountdetail).permit(:first_name, :last_name, :phone, :notes, :id)
end
end
Index.HTML.ERB
<div class="ui card">
<div class="content">
<a class="header"><%= account.first_name %> <%= account.last_name %> </a>
<div class="meta">
<span class="date"><%= account.phone %></span>
<strong><p><%= account.notes %></p></strong> <br>
<%= link_to "edit", edit_accountdetail_path(#accountdetail) %>
<%= link_to 'Inspect', accountdetail_path(#accountdetail) %>
</div>
</div>
</div>
<% end %>
Routes
Rails.application.routes.draw do
get 'welcome/index'
resources :articles do
resources :comments
end
resources :accountdetails
root 'welcome#index'
end
In you index.html.erb replace following
<%= link_to "edit", edit_accountdetail_path(#accountdetail) %>
<%= link_to 'Inspect', accountdetail_path(#accountdetail) %>
with
<%= link_to "edit", edit_accountdetail_path(account) %>
<%= link_to 'Inspect', accountdetail_path(account) %>
#accountdetail was providing you all the records of account, as it was firing select query in controller. But here we need only one instance, so account.
Hope this helps.

Ruby on Rails: how to make a form for associated models (nested)

First of all I have this:
https://polar-scrubland-30279.herokuapp.com/ - my project which is deployed on heroku (Captain Obvious)
I've got projects and todos inside them.
For this moment I show all projects using this way:
------index.html.erb------
<%= render #projects %>
------_project.html.erb-----
<div class="project">
<div class="project-header">
<h2><%= project.title %></h2>
</div>
<div class="project-todos">
<% project.todos.all.each do |todo| %>
<p><%= check_box('tag', todo.__id__, {class: 'icheckbox_square-blue', checked: todo.isCompleted}) %> <%= content_tag :todotext, todo.text %></p>
<% end %>
</div>
</div>
And as you understand it doesn't allow me to change my todo's status when checkbox is checked. So that's why I need a form that will allow me to track all the checkboxes. Also I wanna make text-decoration: line-through when checkbox is pressed, but don't get how to.
Is there a way to creat a form which will satisfy my needs? Please can you help me, Any information will be appreciated.
ADDITIONAL INFORAMTION:
GitHub - https://github.com/NanoBreaker/taskmanager
project.rb
class Project < ActiveRecord::Base
has_many :todos
end
todo.rb
class Todo < ActiveRecord::Base
belongs_to :project
end
Lets start with the models:
class Project < ApplicationRecord
has_many :todos
accepts_nested_attributes_for :todos
end
class Todo < ApplicationRecord
belongs_to :project
end
accepts_nested_attributes_for lets you create or modify several nested Todo records at once when creating or updating a Project.
# will update 2 todos at once
#project.update(
todos_attributes: [ { id: 1, isComplete: true }, { id: 2, isComplete: false }]
)
We can use fields_for to create nested inputs for todos:
<%= f.form_for(#project) do |f| %>
<%= f.fields_for(:todos) do |tf| %>
<%= tf.check_box :isCompleted %>
<% end %>
<% end %>
This generates fields for todos nested under the key todos_attributes. We can whitelist them by using a hash key containing a array of permitted attributes.
class ProjectsController < ApplicationController
before_action :set_project, only: [:show, :edit, :update, :destroy]
def new
#project = Project.new
# this seeds the project with 3 empty tasks
# otherwise we don't have any inputs.
3.times { #project.todos.new }
end
def create
#project = Project.new(project_params)
if #project.save
# ...
else
# ...
end
end
def update
if #project.update(project_params)
# ...
else
# ...
end
end
private
def set_project
#project = Project.find(params[:id])
end
def project_params
params.require(:project)
.permit(:foo, :bar,
todos_attributes: [:isCompleted, :text]
)
end
end
You can create a form for each project by creating a partial which uses a local instead of an instance variable:
# app/views/projects/_form.html.erb
<%= f.form_for(local_assigns[:project] || #project) do |f| %>
<%= f.fields_for(:todos) do |tf| %>
<%= tf.check_box :isCompleted %>
<% end %>
<% end %>
# app/views/projects/index.html.erb
<% #projects.each do |project| %>
<%= render partial: 'projects/form', project: project %>
<% end %>
You can reuse the same partial for the other views as well:
# app/views/projects/new.html.erb
<%= render partial: 'projects/form' %>
# app/views/projects/edit.html.erb
<%= render partial: 'projects/form' %>

polymorphic_path or url_for how customize controller name

I have a situation where I have #challenge and #idea instances from Challenge and Idea model responsively.
I have one partial which is going to used in many controllers, so based on controllers I want to generate path.
e.g
So when I am in IdeasController polymorphic_path([#challenge, #idea]) will generate "/challenges/12/ideas/45"
but if I am in any other controller suppose RefinementsController, I want generate path as "challenges/12/refinements/45"
How can I generate it using polymorphic_path or url_for
code:
app/controllers/ideas_controller.rb
class IdeasController < ApplicationController
before_action :authenticate_user!
load_and_authorize_resource :challenge
load_and_authorize_resource :idea, through: :challenge
def index
end
def show
end
end
app/controllers/refinements_controller.rb
class RefinementsController < ApplicationController
before_action :authenticate_user!
load_and_authorize_resource :challenge, parent: true
load_and_authorize_resource :idea, through: :challenge, parent: false
def index
#ideas = #ideas.refined
render 'ideas/index'
end
def show
render 'ideas/show'
end
end
app/views/ideas/index.html.erb
<div class="ideas">
<% #ideas.each do |idea| %>
<div class="idea">
<div class="title">
<%= link_to idea.title, polymorphic_path([#challenge, idea]) %>
</div>
</div>
<% end %>
</div>
app/views/ideas/show.html.erb
<div class="idea">
<div class="title">
<%= link_to idea.title, polymorphic_path([#challenge, #idea]) %>
</div>
<div class="descriptio">
<%= #idea.description %>
</div>
</div>
Partial
Without having your partial code here, I'm going to make an assumption that you are calling polymorphic_path inside your partial, hence wanting to change the path as per the controller it's loaded from
If you're calling a partial, you have to remember its been designed to be an independent, modular element of your application, and so including #instance variables in your partial will likely be an antipattern
What I would recommend is to populate your partial with local variables, allowing you to populate those accordingly:
#app/controllers/ideas_controller.rb
Class IdeasController < ApplicationController
def show
#challenge = Challenge.find params[:challenge_id] if params[:challenge_id].present?
#idea = Idea.find params[:id]
end
end
#app/views/ideas/show.html.erb
<%= render "shared/partial", locals: {challenge: #challenge, child: #idea} %>
#app/views/shared/_partial.html.erb
<%= link_to "Test", polymorphic_path([challenge, child]) %>
--
Update
Thanks for your code
You'll be totally better be using the following:
#app/views/ideas/index.html.erb
<%= render "ideas/item", collection: #items, as: :item, locals: {challenge: #challenge} %>
#app/views/ideas/show.html.erb
<%= render "ideas/item", object: #item, as: :item, locals: {challenge: #challenge} %>
This will allow you to call:
#app/views/ideas/_idea.html.erb
<div class="idea">
<div class="title">
<%= link_to idea.title, polymorphic_path([challenge, idea]) %>
</div>
<% if action_name =="show" %>
<div class="descriptio">
<%= idea.description %>
</div>
<% end %>
</div>
Although it's not a good practice but if it's only a minor change and rest of your partial is same in all cases then what you do is use some generic variable in your partial. In your case your polymorphic url depends on the initialized value of second variable i.e #idea(as first part challenges/12/ is same) so you can do something like this:
polymorphic_path([#challenge,#something_generic])
and now depending on your controllers action you can change #something_generic to make proper url for your form
class IdeasController < ApplicationController
def new
#challenge = Challenge.find(params[:challenge_id])
#something_generic = #challenge.ideas.build
end
end
class RefinementsController < ApplicationController
def new
#challenge = Challenge.find(params[:challenge_id])
#something_generic = #challenge.refinements.build
end
end
Update:
Change your code to this:
class IdeasController < ApplicationController
before_action :authenticate_user!
load_and_authorize_resource :challenge
load_and_authorize_resource :idea, through: :challenge
def index
end
def show
#something_generic = Idea.find(params[:id])
end
end
class RefinementsController < ApplicationController
before_action :authenticate_user!
load_and_authorize_resource :challenge, parent: true
load_and_authorize_resource :idea, through: :challenge, parent: false
def index
#something_generic = Refinement.find(params[:id])
render 'ideas/index'
end
def show
#something_generic = Refinement.find(params[:id])
render 'ideas/show'
end
end
Since you are using cancancan and have proper filters so #idea and #challenge will be defined automatically in your actions.
For index.html i think you'll be better of using separate templates but if you still want to use a single template then you can do something like this:
app/views/ideas/index.html.erb
<div class="ideas">
<% #ideas.each do |idea| %>
<div class="idea">
<div class="title">
<% if controller_name == "ideas" %>
<%= link_to idea.title, polymorphic_path([#challenge, idea]) %>
<% else %>
<%= link_to idea.title, polymorphic_path([#challenge, #something_generic]) %>
<% end %>
</div>
</div>
<% end %>
</div>
and show.html.erb
<div class="idea">
<div class="title">
<%= link_to #idea.title, polymorphic_path([#challenge, #something_generic]) %>
</div>
<div class="description">
<%= #idea.description %>
</div>
</div>

Single table Inheritance : type defined as a constant

I'm trying tu use Single Table inheritance to represent that employees can be managers or consultants.
So here's my employees model:
class Employee < ActiveRecord::Base
belongs_to :communaute
self.inheritance_column = :fonction
scope :manager, -> { where(fonction: 'Manager') }
scope :consultant, -> { where(fonction: 'Consultant') }
end
and here are my subclasses :
class Consultant < Employee
end
class Manager < Employee
end
When i'm oppening the new view to create an employee. I have undefined method `fonction' error when i'm trying to list the types on employees.
I don't understand why because i defined it in the employee controller.
Could you please help me on this error please.
Below my new form
<%= form_for(#employee) do |f| %>
<% if #employee.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#employee.errors.count, "error") %> prohibited this employee from being saved:</h2>
<ul>
<% #employee.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :fonction %><br>
<%= f.select :fonction, Employee.fonction.map {|r| [r.humanize, r.camelcase]}, {}, disabled: #fonction != "Employee" %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
And my employee controller
class EmployeesController < ApplicationController
before_action :set_employee, only: [:show, :edit, :update, :destroy]
before_action :set_fonction
def index
#employees = Employee.all
##employees = fonction_class.all
end
def show
end
def new
##employee = fonction_class.new
#employee = Employee.new
end
def edit
end
def create
#employee = Employee.new(employee_params)
if #employee.save
redirect_to #employee, notice: "#{fonction} was successfully created."
else
render action: 'new'
end
end
def update
end
def destroy
end
private
def fonction
params[:type] || "Employee"
end
def set_fonction
#fonction = fonction
end
# def fonction_class
# fonction.constantize
#end
def set_animal
#employee = fonction_class.find(params[:id])
end
def employee_params
params.require(fonction.downcase.to_sym).permit(:name, :fonction)
end
end
You need to define fonction in your model (app/models/employee.rb)
The way you're using it implies it's defined as both a class method, and an instance method. It also looks like it could be an array? Employee.fonction.map {|r| ... or an attribute (since you have a select to set it on an employee.)
However, you define fonction as a string...
def fonction
params[:type] || "Employee"
end
So I'm not really sure what you're trying to achieve here... All I know is Employee.fonction looks like a model class method...
def self.fonction
...
end
and f.select :fonction looks like an instance method or attribute?
EDIT I think I've got this... (Sorry, I missed fonction reference in your model definition)
Try adding this to your Employee model:
def self.fonction
["Manager", "Consultant", "Employee"]
end
EDIT #2
You can also define a constant in your model
FONCTIONS = ["Manager", "Consultant", "Employee"]
def fonction
FONCTIONS
end
Did you create fonction column?, create it by entering in your terminal, when in your app directory:
rails g migration add_fonction_to_employees fonction
rake db:migrate
Then check your DB has the column fonction in employees table.

Controller and routes issues in my rails app

I have an app where users can create courses, and each course has_one syllabus. How could I go about configuring my courses and syllabuses (I know it's Syllabi but apparently Rails doesn't) controller, and my routes, so on a course's page there is a link to create or show the course's syllabus, and a link back to the course from the show syllabus page?
In my routes I have:
resources :courses do
resources :syllabuses
member do
put :enroll #this is so users can enroll in the course
end
end
Currently , so the course_id will be saved in the syllabus table in my courses_controller, I have:
def create_syllabus
#course = Course.find(params[:id])
#syllabus = #course.build_syllabus(params[:syllabus])
if #syllabus.save
redirect_to #syllabus, notice: "Successfully created syllabus."
else
render :new
end
end
then in my courses show page I have:
<section>
<% if (current_user.courses.includes(#course) ||
current_user.coursegroups.find_by_course_id_and_role(#course.id, "admin")) %>
<%= render 'create_syllabus' %>
<% end %>
</section>
then in my create_syllabus form (in my courses views folder) I have tried starting it off with:
# I have #course = Course.find(params[:id]) defined in show in the
#courses_controller
<%= form_for #course.create_syllabus do |f| %>
<%= form_for #course.syllabus.create_syllabus do |f| %>
<%= form_for #course.syllabus.create do |f| %>
and I get an undefined method error for each of those.
If you want to create a new syllabus in your show action of a specific course, you can add this to your controllers and views:
courses_controller.rb
#course = Course.find(params[:id])
# Build a new #syllabus object, only if there is none for the current course
unless #course.syllabus
#syllabus = #course.build_syllabus
end
views/courses/show.html.erb
# Show the syllabus name if there is one, or show the form to create a new one
<% if #course.syllabus.name %>
<p>Syllabus: <%= #course.syllabus.name %></p>
<% else %>
<p>Create Syllabus:</p>
<%= form_for([#course, #syllabus]) do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<% end %>
syllabuses_controller.rb
def create
#course = Course.find(params[:course_id])
# Build new syllabus object based on form input
#syllabus = #course.build_syllabus(params[:syllabus])
if #syllabus.save
# redirect to /course/:id
redirect_to #course, notice: 'Syllabus was successfully created.' }
end
end
course.rb
class Course < ActiveRecord::Base
attr_accessible :name
has_one :syllabus
end
syllabus.rb
class Syllabus < ActiveRecord::Base
belongs_to :course
attr_accessible :name, :course_id
end
Some things that I left out but you should still include:
validations
rerendering form if something goes wrong
pulling things out into partials
fixing bad code like if #course.syllabus.name
pull out if/else logic into a helper
…

Resources