Ruby on Rails 4, form inside a view - ruby-on-rails

I am currently trying to build a view that shows a question to the user and if they answer the question right, allows the user to check a checkbox which sends a request to persist that in the database. In other words we keep track of what questions have been answered correctly by the user in the database.
Now my issue is that (besides being a complete newbie in RoR and front end in general), I don't know how to insert the checkbox (form) inside my view along with the question.
I'm using rails 4.
Thank you!

I would start adding a Boolean value to all the answers.
$ rails g migration AddCorrectToAnswers
now that migration should look like
class AddCorrectToAnswers < ActiveRecord::Migration[5.0]
def change
add_column :answers, :correct, :boolean, default: false
end
end
now we can create a new route that will mark the answers correct
# config/routes.rb
Rails.application.routes.draw do
...
resources :questions do
resources :answers do
match "/correct" => "answers#correct", :as => :correct, via: :all
end
end
...
end
Now you should have a new route
question_answer_correct /questions/:question_id/answers/:answer_id/correct(.:format) answers#correct
I am assuming that the answer are in the questions show page
now in your show page you can do something like this
# app/views/questions/show.html.erb
<% #question.answers.each do |answer| %>
<%= answer.answer %>
<%= form_for #user, :url => url_for(:controller => 'answers', :action => 'correct') %>
<%= f.label "Correct Answer" %> <br />
<%= f.check_box :correct %> <br />
....
<%end %>
<% end %>
Now the last thing you have to do is create a method in the answers controller called correct to mark the answers as correct
# app/controllers/answers_controller.rb
class AnswersControlle < ApplicationController
...
def correct
#answer = Answer.find(params[:answer_id])
#answer.correct = true
#answer.save
redirect_to :back
end
end
I hope that this helps
Happy coding

Assuming you already have form setup,
you can add one button which says submit or anything you want to diplay
<button class="check">Click me</button>
Now you can write an event listener for this button which can reveal the answer and if it matches to the answer which User has given, then fire and Ajax call to your backend and save whatever you want to.
example code:
$('.check').on('click', function(){
var real = $('.reveal-answer').val();
$('.reveal-answer').show(); // you need to protect this part from being abused
var answer = $('.user-input-answer').val(); //Assuming its a input field
var questionId = $('.question').data('question-id');
if(answer === real){
url = '/submit_answer';
data = { question_id: questionId, answer: answer };
$.post( url, data , function() {
}, 'json');
}
});
Roughly it should work like this but there are lot to do from the security prospective. For ex: You can not have all answers on view side, You should do the backend validation once the answer is submitted(to check whether it is actually correct or someone tried to hack it.)

Related

Ruby on Rails trying to update a boolean in a user table while logged in as another entity

I am very novice at Ruby and Ruby on Rails.
I am trying to update a web application that has signed up volunteers that require approval before they can access full website functionality. I added a boolean field to the volunteers database model called :approved that defaults to false when a volunteer signs up, and requires a logged in administrator to update it to true.
I created an administrator class in my project that can view a list of pending volunteers from the controller:
def view_pending_volunteers
#navbar = true
#volunteers = Volunteer.where(approved: false)
end
In the administrator view I want to use checkboxes associated with volunteer, which when submitted will allow the administrator to update the :approved field in the volunteer table.
I have several issues that are not clear to accomplish this task:
In my administrator model I use has_many :volunteers with a migration that put the volunteer_id as a foreign key in the administrator database table. Is that sufficient to accomplish the task at hand, or do I need to create a join table for :approved? I can't have volunteers belong_to :administrators because they would not be able to sign up on their own, they need to be created by administrators.
I am not sure exactly how to configure the code for checkbox helpers in my administrator view. I am using form_with but I am sure my code is not correct. I would like to pass an array of parameters for each check_box associated with the :approved field for the list of pending volunteers back to the controller.
<p><b style="color:blue;font-size:24px;">
<%= form_with(model: #volunteer, local: true) do |f| %>
<% #volunteers.each do |v| %>
<li>
Volunteer: <%= x.first_name%> <%= x.last_name%>  Age: <%= x.age%>  Description: <%= x.description%> 
<%= f.check_box (#volunteers, v.approved, options = {}, checked_value = "1", unchecked_value = "0")%>
</li>
<% end %>
<%= f.submit "Approve" %>
<% end %>
</b></p>
I am not exactly sure how to handle the array of checkbox values that get returned to the administrator controller in order to update the volunteer database table. Do I need to create a hash in the view (how to do that?) and then loop through the hash to update_attribute(:approved, true) for each checked volunteer? I have created an update_pending_volunteers method in the administrator controller for the POST operation, but am unclear on the code that should be there because I am unsure the checkbox approach.
Thanks in advance for your patience with a newbie. This seems like such a simple thing to do but I am not sure of the proper approach. I have spent quite a bit of time reviewing APIs and videos and stack overflow articles but cannot seem to piece together information that will give me confidence in a particular approach to do this correctly. Again it seems like such a simple thing to accomplish but has become a source of frustration.
See the comment from Rockwell Rice. Don't create a relationship of any kind for this functionality.
Use the URL feature of form_with, not model. You're not acting on one volunteer, you're acting on many. Create a post route (ie. approve_volunteers). In the view you would create the checkboxes like this:
<%= form_with(url: approve_volunteers_path, local: true) do |f| %>
<% #volunteers.each do |v| %>
<div class="field">
<label class="checkbox">
Volunteer: <%= x.first_name%> <%= x.last_name%>  Age: <%= x.age%>  Description: <%= x.description%> 
<input type="checkbox" name="volunteers[]" value="<%= v.id %>">
</label>
</div>
<% end %>
<%= f.submit "Approve" %>
<% end %>
That should send params through the form like {"volunteers" => ["1", "3"]} and leave the rest empty. You might have to play around with those a little. Check your console for the params.
Then in your controller something like:
def approve_volunteers
volunteer_ids = []
volunteers = Volunteer.where(approved: false)
volunteers.each do |v|
if v.id.in?(params[:volunteers])
volunteer_ids << v.id
end
end
Volunteer.where('id IN (?)', volunteer_ids).update(approved: true)
end
See above.
The checkbox code provided by Sam worked perfectly. My url for the view is "update_pending_volunteers_path." I need to improve the formatting a little bit.
The code in the controller that worked to loop through the array of volunteer ids that was passed back into the controller is as below:
def view_pending_volunteers
#navbar = true
#volunteers = Volunteer.where(approved: false)
end
def update_pending_volunteers
#navbar = true
params[:volunteers].each do |value|
Volunteer.find(Integer(value)).update(approved: true)
end
redirect_to (administrator_dashboard_path)
end
The volunteers passed into the view have already been parsed to just those that have not been approved, so the volunteer_ids returned as checked are only from that list.

Rails: Having a button in a view call a method in a controller, route not working

Hi I'm making a rails app that uses Zendesk API calls. I have a controller that uses two classes I defined
class TicketsController < ApplicationController
require 'ticket_fields'
require 'ticket_search'
def getTickets
#search_fields = SearchFields.new(client)
#tickets = TicketSearch.new(client)
end
def search_tickets
#ddcustomvalues = [params[:customer_company_id], params[:study_id], params[:type_id], params[:system_id]]
#tickets.search_tickets(ddcustomvalues)
end
end
One class SearchFields uses the api to load values I want to filter tickets by into arrays. My view then uses these values to populate drop down lists.
The other class TicketSearch looks like this.
class TicketSearch
attr_reader :tickets, :text
def initialize(client)
#text = "query"
#tickets = Array.new
client.tickets.all do |resource|
#tickets << resource
end
end
def search_tickets(custom_search_fields)
querystring = "type:ticket+tags:"
custom_search_fields.each_with_index do |field, index|
unless field == ""
if index ==0
querystring += "#{field}"
else
querystring += " #{field}"
end
end
end
#text = querystring
end
end
What I want to happen in my view is when a button is pressed it changes the value of #text to the querystring generated by the drop down list options that were selected. I'm currently doing this for testing to see if my querystring is correct and the button works. What I eventually want it to do is send the querystring to the ZenDesk Server and returns the tickets I filtered for. the #tickets array would then be replaced with the filtered tickets the server returned. Currently my button code looks like this.
<%= button_to 'Search', :action => 'search_tickets' %>
with all the route code I've tried I either get an error upon starting the page. Or when I press the button nothing happens and the #text being displayed in my view remains "query". Can someone help explain what I need to do I don't quite understand how routes work.
==================================================================================
Hey so I made the changes you suggested and did some reading up on AJAX and js and I think I'm almost at the answer my view now looks like this
<div id="test" >
<%= render partial: 'text', locals: { text: #tickets.text} %>
<div id="test" >
and I created a partial _text file that looks like this
<p> Query: <%=text%> </p>
and a js file search_tickets.js.erb
$("#test").html("<%= escape_javascript(render partial: 'text', locals: { text: #tickets.text } ) %>");
any idea what may be going wrong everything loads up okay but the text remains the same in the partial i set up when i hit the button still
the console outputs this after the button is hit
ActionController::RoutingError (No route matches [POST] "/tickets/search_tickets"):
so I guess it may actually be a routing error my route looks like this
resources :tickets do
collection do
put :search_tickets
end
end
and the form tag calling the path looks like this
<%= form_tag search_tickets_tickets_path, remote: :true do %>
<table>
<tr>
<td align = "left" valign="middle"> <font size = 4> Customer Company </font> </td>
<td align = "left" valign="middle">
<%= select_tag "customer_company_id", options_for_select(#search_fields.customer_companies), :prompt => "Select One" %>
</td>
</tr>
......
<tr>
<td> </td>
<td align = "left" valign="middle">
<%= submit_tag "Search" %>
</td>
</tr>
</table>
<% end %>
==================================================================================
(Update)
I think I fixed my last problem by changing my form tag to this
<%= form_tag search_tickets_tickets_path(#tickets), method: :put, remote: :true do%>
however now I get this error from the terminal after I hit the button
NoMethodError (undefined method search_ticket' for nil:NilClass):
app/controllers/tickets_controller.rb:15:insearch_tickets'
how would I pass #tickets as a parameter through my route because clearly its not accessible by search_tickets right now as its giving a nil class error.
Variables
when a button is pressed it changes the value of #text to the querystring generated
It looks to me like you're confused with the stateless nature of Rails - in that, just because a view has been rendered doesn't mean the values / variables are still available for use.
It was mentioned in the comments that it seems you're basing a lot on experience with other frameworks / programming patterns. The best way to describe your solution is that Rails has to "refresh" all your variables / values each time it processes a request; consequently meaning that if you send a button request - you'll have to perform the request as if it were the first one
Ajax
The bottom line is that you need to use an ajax request to pull this off.
To do this, you'll be be best creating a form (not just a button_to), as this will give you the ability to send as many params as you want. You should use form_tag:
#config/routes.rb
resources :tickets do
collection do
get :search_tickets
end
end
#view
<%= form_tag tickets_search_tickets_path, remote: :true do %>
... #-> fields for your params
<%= submit_tag "Search" %>
<% end %>
This will give you the ability to define the following in your controller:
#app/controllers/tickets_controller.rb
Class TicketsController < ApplicationController
def search_tickets
#ddcustomvalues = [params[:customer_company_id], params[:study_id], params[:type_id], params[:system_id]]
#tickets.search_tickets(ddcustomvalues)
respond_to do |format|
format.js #-> loads /views/tickets/search_tickets.js.erb
format.html
end
end
end
#app/views/tickets/tickets_search.js.erb
//JS here to manipulate your original page
Requests
The bottom line here is that if you want to "manipulate" your view without refreshing, unlike "native" application frameworks, where you can rely on a persistent state, with Rails, you basically have to construct the request from scratch (IE passing all the params required for the method to run)

Use ActiveAdmin to edit a single record

I have a model that needs editing that is associated with the current user called BillingProfile. How can I add a menu item that links to the edit page of the current user's BillingProfile? I don't want or need an index page for the BillingProfile as a user can only edit their own.
class User
has_one :billing_profile
end
You can use Cancan to manage the ability to allow a User to edit his own Billing Profile.
ability.rb
...
cannot :edit_billing_profile, User do |u|
u.user_id != user.id
end
...
admin/users.rb
ActiveAdmin.register User do
action_item :only => :show do
link_to "Edit BP", edit_bp_path(user.id) if can? :edit_billing_profile, user
end
end
Or you can try something like this:
ActiveAdmin.register User do
form do |f|
f.inputs "User" do
f.input :name
end
f.inputs "Billing Profile" do
f.has_one :billing_profile do |bp|
w.input :address if can? :edit_billing_profile, bp.user
end
end
f.buttons
end
end
I have not test it, but I did something similar on a project.
This may help you-
Adding custom links:
ActiveAdmin.register User, :name_space => :example_namespace do
controller do
private
def current_menu
item = ActiveAdmin::MenuItem.new :label => "Link Name", :url => 'http://google.com'
ActiveAdmin.application.namespaces[:example_namespace].menu.add(item)
ActiveAdmin.application.namespaces[:example_namespace].menu
end
end
end
I basically created a new ActiveAdmin::MenuItem and add it to the current ActiveAdmin menu with the namespace example_namespace and return the menu in the end of the current_menu method. Note: current_menu is a method expected by ActiveAdmin so don't change the name of it. You can add as many items you like and each of these items will be converted to a link on your navigation header. Note this works for ActiveAdmin version > 0.4.3 so you might need to do your own digging if you want to do it for version <= 0.4.3.
I have a LinkHelper defined which has the following two methods:
#This will return an edit link for the specified object instance
def edit_path_for_object_instance(object_instance)
model_name = object_instance.class.to_s.underscore
path = send("edit_#{model_name}_path", object_instance)
end
#This will return an show link for the specified object instance
def show_path_for_object_instance(object_instance)
model_name = object_instance.class.to_s.underscore
path = send("#{model_name}_path", object_instance)
end
You can call the edit_path_for_object_instance method directly from your view and pass in the user.billing_profile object.
This will give you a link directly to the entity resulting a url like /billing_profile/ID/edit
An alternate approach is to use fields_for. This will allow you to create a form for the User attributes and update the associated BillingProfile at the same time. It would look something like this:
<%= form_for #user%>
<%= fields_for #user.billing_profile do |billing_profile_fields| %>
<%= billing_profile_fields.text_field :name %>
<% end %>
<%end%>
See here: http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html

how to connect my model to my app

Hey all,(im a beginner in rails)
i've created a controller that look like that:
class HomeController < ApplicationController
def homepage
end
def showmsg
#postword = params[:p]
end
end
the showmsg view looks like that:
<%= #postword %>
and my homepage view looks like that:
<%= form_tag( {:controller => 'home', :action => 'showmsg'}, :method => "post") do %>
<%= text_field_tag(:p,#postword) %>
<%= submit_tag("post") %>
<% end %>
now i have a form that i can write something in it and it will show on the showmsg view.
i created a model with the param :posts with a :description "text" field too.
MY QUESTION is how do i implement the model in the code so any thing i write will be in a list with the things i wrote before, because now (obviously) anything if i write something its deleting the one i wrote before.
thank you all!
I would argue that you're approach is not very rail's like... so if you're learning rails... you're learning it wrong.
Make a Model. Call it "Message":
rails generate model Message content:string
remember to migrate (hopefully you have your databases setup properly):
rake db:migrate
Then in your controller, when you post, you can create message like this:
def create #instead of showmsg... 'create' is the standard name for this
Message.create(params[:message])
#messages = Message.all
end
This will create the message in the database, and then it will get all the messages out of the database and put them into #messages.
You need to edit your form so that it uses form_for. You need to pass it #message, which is an instance of Message.new that your first controller action created. You should call this new
In your create.erb.html file, you show all the messages like this:
<ul>
<% #messages.each do |message| %>
<li><%= message.content %></li>
<% end %>
</ul>
I actually wouldn't recommend showing all the messages in the create action - it should really happen in the index action and you should redirect... but we need to keep this simple. Just google this or watch some of Ryan's screencasts and you'll get it.
And you're done. This is the "Rails Way" to do things. It's best to learn it the way they want you to learn it.
I would also commend that you format your code properly by indenting, and start naming your methods to be real english. For example, showmsg is bad and show_message is a lot better.
If all of this is totally confusing, then just create a new project, and then type:
rails generate scaffold message content:string
It will basically build the application you want and a lot more. You can just read the code and see how they did it.
Hope it helps.
Your approach is not really rails like so some tweaks and fixes are needed. Suggestions: check rails approach to REST. The following code will work it is a little more rails like, but still not all the way there.
Generate a model
rails generate model Message postword:string
this will generate the model and create the migration necessary to create the table in the database.
Create the table
rake db:migrate
Define a post action
It will save the postword in the database. In your controller:
def create
#message = Message.create!(params[:message])
if #message.save
redirect_to "/home/showmsg"
else
render :action => "/home/homepage"
end
end
Create and instance of Message to use in your form
def homepage
#message = Message.new
end
Fix your form tag
<%= form_for #message, :url => "/home/create" do |f| %>
<%= f.label :postword %>
<%= f.text_field :postword %>
<%= f.submit "Create" %>
<% end %>
Now let's show the words in the showmsg page
In the controller select the postwords from the database:
def showmsg
#postwords = Message.all
end
Showing them: /showmsg.html.erb
<H1>postwords list</H1>
<ul>
<% #postwords.each do |p| %>
<li><%= p.postword %></li>
<% end %>
</ul>
Your routes.rb file will have this routes:
get "home/homepage"
get "home/showmsg"
post "home/create"
Define an attribute :new_text in a way similar to this:
class TheModel
# Virtual writer - everything assigned to this attribute
# will be added to self.text
#
def new_text=(v)
self.text += v.to_s
end
def new_text
"" # This is write-only attribute
end
end
Now, use the field 'new_text' in your form.
Of course, this is a very simple example. You should decide whether you want to add the content on every call to :new_text=, maybe some validation would help, the read accessor may need some care, and so on.
For some good guides which may help you start, see the site http://guides.rubyonrails.org/

Identifying a item in one of multiple ways

I'm designing a form where you need to add a relation to another object. Okay that's normally fine, but what I am hung up on is a clean way to make it easy for the user to enter the object. There are multiple ways that the user could know how to specify the object (unique identifiers).
Let's take the example of associating a user to a task. In this case, the models are laid out like this:
class User
has_many :tasks
# fields: phone_number, email, username
validates_uniqueness_of :phone_number
validates_uniqueness_of :email
validates_uniqueness_of :username
# other methods, validations, etc which are not important.
end
class Task
belongs_to :user
# other methods, validations, etc which are not important.
end
How would I write the controller and view form, if I want to be able to specify the user by username, email, or phone_number - I might know any one of these, and just one is enough to specify exactly the user that I want, in a clean way?
Currently my solution seems messy. I have a view similar to:
<% form_for #task do |f| %>
... Other stuff
User - choose one of the following ways: <br />
Username: <%= text_field_tag :user_name %> <br />
or phone number: <%= text_field_tag :user_phone %> <br />
or email: <%= text_field_tag :user_email %> <br />
... More other stuff
<% end %>
I then handle these fields explicitly in the controller, finding the actual user based on which ones are filled in:
class TasksController
def create
#task = Task.new(params[:task])
if params[:user_name]
#task.user = User.find_by_username(params[:user_name])
elsif params[:user_phone]
#task.user = User.find_by_phone_number(params[:user_phone])
elsif params[:user_email]
#task.user = User.find_by_email(params[:user_email])
end
if #task.save
redirect_to #task
else
render :action => 'new'
end
end
end
This seems like it's very specific, and there is a lot of code in my controllers, especially if I have lots of these on a form. Don't even talk to me about when you need to dynamically add multiple users to a task - it gets even more crazy in the controller parsing everything out.
I think the most unobtrusive way of doing this is with a few AJAX calls.
I'm thinking an observer on a text field that calls a remote function. That function should update your form with matching potential associations, that the user can select with a radio button or something.
It would look like something like this:
UserController:
def select_user
#user = case params[:query]
when !/\w/ #phone no
User.find_by_phone(params[:query])
when /#/ # email
User.find_by_email(params[:query])
else
User.find_by_username(params[:query])
end
end
views/select_user.rjs:
page.replace_html :matched_user, :inline => <<"PARTIAL"
<%=hidden_field_tag "task[user_id]", #user.id%>
User: <%=#user%>
PARTIAL
task form
<%form_for #task do |f|%>
...
<%= text_field_tag :query %>
<%= observe_field :query, {:controller => :users, :action => :select_user}%>
<div id="matched_user" />
<% end%>
May not work as advertised. I made assumptions and haven't tested it. But it should put you on the right track.
When it comes to multiple users all that really needs to change is the hidden field tag and the rjs file. but not by much. There's also nothing stopping you from using a more robust search mechanism.
Phone numbers are going to be a problem, unless you process the input to strip out hypens, parentheses, etc.
Having said that, here are a couple of options.
1 Roll your own.
In User:
def find_by_foo(search)
# TODO: Pre-process anything in search that looks like a phone number.
conditions = "username like ? or phone_number like ? or email like ?"
wildcard_search = "%#{search}%"
substitutions = []
3.times do
substitutions << wildcard_search
end
User.find(:first, :conditions => ([conditions] + substitutions))
end
2 Rolling your own is stupid when searchlogic already exists.
From their examples under "combining scopes":
User.username_or_first_name_like("ben")
=> "username LIKE '%ben%' OR first_name like'%ben%'"

Resources