Click rails button to permanently change data model and css - javascript

So I'm creating a photo proofing web app for a client. I want him to be able to skim the images on the site and under each image is an 'Approve' button, which he can click and the image border will turn green, signaling it's good for export.
Now my JS/Jquery knowledge is pretty limited, but I know that this might go deeper than just front-end work, because I want to see those changes even after browser is closed, which I think requires a both back-end and front-end solution.
I'm thinking to create a boolean attribute under my image model, and when you click the 'Approve' button, it'll switch the boolean value to true, which will change the css class to turn green. Is there a way rails can detect a boolean value and change the css accordingly? I want to be able to see the changes my client made.
Would appreciate any feedback/advice on my approach, or if there's a better way out there to go about this. Thanks!

first add on your images table an approved column with type boolean
and on your images controller and these action
def approve
#image =Image.find(params[:id])
#image.update_column(:approved,true)
respond_to do |format|
format.js
end
end
in routes add these method
resources :images do
member do
put :approve
end
end
in your html
<div class="image">
<img src="<%=#image.source%>" />
<%= link_to "approve",approve_image_path(#image),:remote=>true,:method=>"PUT",:class=>"approve_me"%>
</div>
in your image.js file add these method
$("body").on("click",".approve_me",function(e){
e.preventDefault();
$(this).parents(".image").find("img).css("border-color","green");
});

I think you are almost there.
Firstly - yes, changing the css with javascript on runtime will immediately change the appearance of dom elements.
On the client side - you want to indicate to the user which images have been selected, but you also need to store the approved value for each element in your submit form.
In Rails it is common to create hidden input elements storing additional data. Given the task at hand - they can store a value of 0/1 - rejected/approved. You can come up with your own convention for naming the image/select values.
Then, in your view, add onclick listeners to your images pointing to a javascript function:
1) checking if the clicked element is already selected,
2) changing the css of the current element,
3) updating the hidden input value for that element.
Here is a dummy demonstration - jsfiddle
Later, you can then get the approved/rejected value from the params in your controller, like (see below).
In case you want to assign a css class when constructing the view and depending on an approved flag value, you could do something like:
<img id="image_<%= image_id %>" src="<%= image_item.path" class="<%= (image_item.approved.to_i == 1) ? 'approved_img' : 'non_appr_img' %>" %>
<input id="image_<%= image_id %>_app_flg" type="hidden" value="<%= image_item.approved %>" />
where image_item is an object properties path and approved (self explanatory), image_id the id of the image object, approved_img and non_appr_img - css classes.
I'm not discussing the back-end for storing the approved flag, as it seems it is out of the scope of the question
EDIT
Brief concerning back-end
Given the you have an images model, extend it to include an approval property (prepare a database migration and edit your model .rb file to include the new columns).
In the view, include all of the hidden inputs inside a form which will be submitted to your controller(for example looping through an array on your images). For example:
<%= form_for :images, :url => {:action => "approve_images"}, :html => {:name => "testForm"} do |f| %>
<!-- TODO do stuff here - eg display images -->
<% #images.each do |imageItem| %>
<%= f.hidden_field "#{imageItem.id}_appproved", {:value => imageItem.approved}%>
<% end %>
<!-- TODO add a submit button -->
<% end %>
*You need to
Here :images is the controller, approve_images is the function in the controller that the form will be submitted to (include in routes), #images is an array with images data (from your model) and that you have prepared in the controller before rendering the view. I assume the images have ids and approved property.
This will yield in your view dom elements like this:
<input id="images_IMAGEID_appproved" name="images[IMAGEID_approved]" type="hidden" value="1" />
After submitting the form, in your controller you will be able to access these values like this:
img_approved = params[:images][IMAGEID+"_approved"]
Finally you can store that value to your database. Good luck! :)
I am omitting a lot of even more basic things, but I think the question is too broad as it is and there are plenty of resource detailing how to create,read,write a model, prepare data for the view etc. If not - please get started with http://guides.rubyonrails.org/getting_started.html

Thanks everyone who helped me answer this question below! I came up with a solution I'm pretty happy about, figured I'd share it hoping it'll help somebody else along the way.
The problem: I was looking for an AJAX solution that could make permanent changes to the data model. I wanted somebody to be able to toggle/highlight certain items on a page, and have those changes saved on the backend so I can view it later.
This required both a front-end ajax solution for the user interface and back-end solution so the ultimate changes will be saved in the data model so when I load up the site later, I can see the changes he made.
My solution (with the help of those who answered below):
Backend - I created a link that when pressed, would toggle the attribute in my model either true/false
Frontend - In order to give the client a real-time feel, I had to set the link to perform ajax requests and change the css accordingly.
My controller:
def approve
#pipe = Pipe.find(params[:id])
respond_to do |format|
if #pipe.toggle!(:approved)
format.html { redirect_to root_url }
format.js
else
format.html { render :index }
end
end
end
My pipes table has an approved:boolean attribute
My approve.js.erb file (I wrapped each pipe in a div using div_for(#pipe):
<% if #pipe.approved? %>
$('div#<%= dom_id(#pipe) %>').children('.flexslider').css('border','4px solid green');
$('div#<%= dom_id(#pipe) %>').children('a').text('un-approve');
<% else %>
$('div#<%= dom_id(#pipe) %>').children('.flexslider').css('border','4px solid white');
$('div#<%= dom_id(#pipe) %>').children('a').text('approve');
<% end %>
My Application Helper:
def approve_text(approvable)
approvable.approved? ? 'un-approve' : 'approve'
end
My trigger link (which uses abovementioned helper):
<%= link_to approve_text(pipe), approve_pipe_path(pipe), remote: true, method: 'PUT', class: approve_text(pipe) %>
My routes:
resources :pipes do
member do
put :approve
end
end
Again, thanks for those who helped provide answers. This is a solution I'm pretty happy with. I know it probably could use some help being refactored. If anyone has suggestions, would love to hear about it!

Related

How to update single column using modal on one page based on ajax request

I have page who contains 3 boxes and all of these boxes's data need to update in one db table so I used to update all of them one by one using partial and custom ajax.
View Code:
.col-lg-3.col-md-3.col-sm-6.col-xs-6
.box-bar
h5.prodman Short Description
br
btn.btn-primary.btn-lg
= link_to 'Edit', edit_product_path(#product, field: 'sd'), remote: true
.col-lg-3.col-md-3.col-sm-6.col-xs-6
.box-bar
h5.prodman Long Description
br
btn.btn-primary.btn-lg
= link_to 'Edit', edit_product_path(#product, field: 'ld'), remote: true
.col-lg-3.col-md-3.col-sm-6.col-xs-6
.box-bar
h5.prodman T&Cs (General, Privacy, Fair Usage etc)
br
btn.btn-primary.btn-lg
= link_to 'Edit', edit_product_path(#product, field: 'tc'), remote: true
Upon clicking link_to the modal loads all those content:
edit.js.erb code:
<% field_name, field_title = get_field_name_title(params[:field]) %>
$('#dialog h4').html("<i class=' glyphicon glyphicon-pencil'></i> Update <%= field_title %>");
$('.modal-body').html('<%= j render('terms_field_form',{field_name: field_name}) %>');
$('#dialog').modal("show");
$('#dialog').on('shown.bs.modal', function () {
CKEDITOR.replace('<%= "#{field_name}" %>');
});
$(document).on('click', '.update_terms', function () {
$.ajax({
type: 'PATCH',
dataType: 'script',
url: '<%= product_path(#product) %>',
data: {
"authenticity_token": $('input[name=authenticity_token]').val(),
"product": {"<%= field_name %>": $('.terms_content').val()}
}
});
});
Partial Code:
= text_area_tag "#{field_name}".to_sym, #product.send(field_name), class: 'form-control terms_content', id: field_name
= button_tag 'Submit', class: 'btn btn-primary mg-t-20 mg-b-20 mg-r-10 update_terms'
Dynamic fields (column and titles ) code:
def get_field_name_title(field)
return 'short_description', 'Short Description' if field == 'sd'
return 'long_description', 'Lease Terms' if field == 'ld'
return 'terms_and_conditions', 'T&Cs (General, Privacy, Fair Usage etc)' if field == 'tc'
end
Problem
The boxes contents always remain same. Means, I am updating 'Long Description' and I will update it in db but if I try to update any other box it show the name of that box again ( the previous one I updated ).
I got an impression that on each click and updation the modal stay same and on next click its adding with existing once. And it iterates it to next click. So, click1, next time I clicked2, so click1,click2. then next time i clicked3, so click1,click2,click3. this is the issue.
So, no new fresh event to new click.
Is there any proper way to do it if my process lags any feature?
You are having issues because you haven't fully committed to the single page paradigm. I'm not sure of the interaction with CKEDITOR, but the 3 warning signs I see ...
1) Your update doesn't work, but create does
2) Your code is randomly populating all of the fields with the same data
3) Your code has no unique identifier for the fields in the css/html
You need to uniquely identify the fields which will be altered.
Note: all of this assumes that you are intending to treat each field separately as it's own form - I'm not sure you make a conscious decision about which strategy to use - so I'm just following the lead of the questions you seem to be asking - as I said in the rails/ruby chat - the simplest way is to make it all one form, have a single update button for it and be done
Your div or id for the html code needs to reflect this fact, by being wrapped around those modal fields.
Which will allow you to use to jquery .bind & attach a trigger on just that field to run the update against server.
... example of us setting up a html class/div to anchor the jquery too ...
Old:
.col-lg-3.col-md-3.col-sm-6.col-xs-6
.box-bar
h5.prodman Short Description
br
btn.btn-primary.btn-lg
= link_to 'Edit', edit_product_path(#product, field: 'sd'), remote: true
New with class - (not tested but general idea):
.sd-class # -- note this can be any name, as long as sd/ld/tc aren't the same
.col-lg-3.col-md-3.col-sm-6.col-xs-6
.box-bar
h5.prodman Short Description
br
btn.btn-primary.btn-lg
= link_to 'Edit', edit_product_path(#product, field: 'sd'), remote: true
Next your simply setup an jquery binding based on that anchor - answered thousands of times - Modal updating with jquery.
I'm guessing here - but CKEDITOR should be able to take that modal field that is properly pointed too inside the anchoring div I pointed out to you ... see this answer for how to test and locate an element inside another to feed it the right field.
Note: the other part is that CKEDITOR MIGHT need to be set to nil/initialized each time you call it, to ensure blank or set to the field using the unique class div we setup as an anchor
See this answer for help selecting the right field with jquery
I feel like most of your issue is the code you wrote doesn't explicitly tell CKEDITOR where to find the information it's targeting & it's not reseting after each run. So you might have to initialize CKEDITOR each time - see their forums for help on that one.

Rails: ActionView::Template::Error while removing row from table using designated .js.erb

I need a help with removing rows from a table using jQuery in Rails 4.2. Once I click on the green button, I want to have the entire row deleted and the table refreshed.
As for the logic, I am working with documents and users. A user is able to hide a document and attach a note to specific hiding. Therefore, I've decided to create additional model Hiding, which contains references to User and Document along with other relevant information:
hiding.rb
class Hiding < ActiveRecord::Base
belongs_to :document
belongs_to :user
end
From the other perspective, Document and User models have the following association:
has_one :hiding
After clicking the green button, the controller's unhide action gets called:
class Admin::HidingsController < AdminController
authorize_resource
def index
#hidings = Hiding.all.page params[:page]
end
def unhide
#hidings = Hiding.all.page params[:page]
#hiding = Hiding.where(document_id: params[:id])
# update document and delete hiding from DB
search_un_hide_document(false, nil)
end
end
However, when I want to perform deletion of table row via unhide.js.erb
$('#<%=#hiding.document.id%>').remove()
the following exception is triggered:
ActionView::Template::Error (undefined method `document' for #Hiding::ActiveRecord_Relation:0x0000000c4508d8>)
I've been stuck on this for some time as I am a newbie in Rails. Could not figure out, how to properly remove those rows without re-rendering entire page again, though.
Your suggestions would be appreciated with big gratitude. Thanks in advance!
def unhide
#hidings = Hiding.all.page params[:page]
#hiding = Hiding.where(document_id: params[:id]).first
# update document and delete hiding from DB
search_un_hide_document(false, nil)
end
Probably you forgot to add .first for #hiding
Edited:
Instead of where().first, you can also use find_by(document_id:
params[:id])
find_by is defined like this
def find_by(*args)
where(*args).take
end
Now, 'take' differs from 'first' in regards to order. first returns the first record according to the order of the primary key while take returns whatever the database spits out first.

Rails: Dynamic form generation from the database

I've been scratching my brain (kinda painful, wouldn't advise it) trying to figure out a way to pull this off, but I haven't been able to come up with anything that really seems feasible.
The idea is this: I have a page that allows you to create an event. This event could be anything from a wedding to a marathon or fund raiser. Different events need to have a form specific to that event type, e.g. the wedding event needs the name of the bride, groom, address, receptions, etc.
What I'm trying to figure out is how to allow them to select an event type, and then go to localhost:3000/events/create(/:event_type) and have that view load in the correct form fields.
My original thought was to have event types stored in a database with each form stored as JSON, but this just seems more complicated than I'd think necessary.
Any ideas?
Here's routes.rb so far:
get 'my_events/', to: 'events#index', as: :events
get 'events/create', to: 'events#create', as: :new_event
match 'events/new', to: 'events#new', as: :post_new_event, via: :post
get 'event/:slug', to: 'events#show', as: :show_event
root 'static#index'
devise_for :users, :controllers => { :omniauth_callbacks => 'omniauth_callbacks' }
And the events controller:
class EventsController < ApplicationController
def index
#events = Event.all()
end
def create
#event = Event.new()
end
def show
#event = Event.find_by slug: params[:slug]
end
def delete
end
end
You can generate a form with tags to match any number of columns without knowing the column names like this
<%= form_for #event do |f| %>
<% #event.attributes.each do |k,v| %>
<% unless k == 'id' %> #in the case of an edit form, the id should not be changable
<p>
<%= f.label k.to_sym %><br> #get the name of the column, put it into a symbol
<%= f.text_field k.to_sym %> #same as above, will put the matching value in if it exists
</p>
<% end %>
<% end %>
<%= f.submit %>
<% end %>
Though this may not be suited for how your model appears to be set up, a single Event database that can hold any event. This would work better if you had a specific database for each type.
To go in to detail on how I would see this working for you...
Your planned route /events/create/:event_type would work
in your new action in the controller
def new
if params[:event_type] == 'wedding'
#event = Wedding.new #would need to do the same for each event type
end #a model/database for each different type of event
end
However if event is a parent Event database to a child event type, you may be able to make it work, though I'm not familiar enough with relational databases in Ruby on Rails to say how.
This is a fairly common pattern. On your edit/new form(or modal), you need to have a javascript handler on your event_id field. When it changes, you do an ajax request to your server to get the valid event_types for that event so that you may populate the event_types input control.
I would strongly discourage doing any client side storage (hard coding) of the event_types as this design will not allow for easy addition of future events.

How to write form to has_many association without nested_form?

I have a Patient model. And I want be able as a Patient adds multiple Doctors.
Usually how I do it by creates with nested_form for Patient(without any of his attributes)
# #patient = current_patient
= simple_nested_form_for #patient do |form|
= form.simple_fields_for :doctors, #patient.doctors do |f|
= f.input :first_name
= f.input :last_name
= form.link_to_add :add, :doctors
= form.button :submit
And I am using PatientsController *#update* action to add and remove doctors from them.
I have already one PatientsController with edit action to edit Patient information.
I think right controller to add multiple Doctors to Patient should be DoctorsController. But this will not work with form above which always update Patient object.
So how this form should looks like? What is correct pattern for that?
Gem: https://github.com/ryanb/nested_form
Checkout Ryan's railscast #196 Nested Model Form (revised) where he shows what you are trying to do. Basically, instead of Patient and Doctors he uses Question and Answers. Jump to minute 10:00 to see it in action and confirm that is what you are looking for. The correct controller is Patients in this example.

Ruby on Rails - call Controller Method and pass Parameters to it

I have following situation:
I have a risk model and on my view it shows me a table with all risks. The table also includes a check_box_tag, as each risk should be able to be checked. In the tablehead there is a button and if this button is clicked, a method of the risk controller should be called, and it should have all checked risk IDs as parameter.
Actually, I don't know which would be the best way to solve this.
I have following code so far:
View:
<table>
<thead>
<tr>
<th>Risk Name</th>
<th>
<button id="mergerisks">Merge Risks</button>
</th>
</tr>
</thead>
<tbody>
<% Risks.all.each do |risk| %>
<tr>
<td><%= risk.name %></td>
<td><%= check_box_tag 'mergerisks', risk.id %>
</td>
</tr>
<% end %>
</tbody>
</table>
Javascript:
$( "#mergerisks" ).on( "click", function() {
var selectedriskmerge = new Array();
$("input:checked").each(
function() {
selectedriskmerge.push($(this).val());
});
});
Edit
Added the following ajax call to javascript
$.ajax({
url: '/riskmerge',
data:selectedriskmerge,
success:function() {
window.alert("Success!");
}
});
For now the Button only triggers the Javascript, and there are the ID's of all checked Risks are stored in an array.
But now I don't know what would be the best way to call a controller method in the risk controller and pass the IDs of all checked Risks to the method.
Thanks for any help.
NOTE: This is slightly different from what you wanted, but might provide a better user experience since they won't have to click an additional button at the end to update the records so heed with caution. No matter what though you will get a decent example of how to use ajax for your own needs.
First you need to create an action in the controller that updates the record you want which you already have. The twist is that instead of rendering html you will want to render json at the end. Something like this will do.
if risk.update_attribute(check: true)
render :json => { status: "Everything worked!"}
else
render :json => { status: "Something went wrong!"}
end
Next you will want to set up the javascript so that when a check box is clicked, an ajax post is sent to the action that updates the record. You have this partially done with your javascript. Inside your click event, you can have something like
$.post("/risk_update",
// This line below is the parameters sent to the action.
//So your action will recognize params[:risk_id]
{ risk_id: $(".clicked_check_box").attr("value") },
"json")
.done(function (responseText) {
if (responseText.status === "Everything worked!") {
// Do something on success of info being saved
} else if (responseText.status === "Something went wrong!") {
// Do something on failure of info being saved
}
});
Finally, there is a problem with the check box. Do you want the user to uncheck the box and call a record again. This of course goes beyond the discussion of the question but they are some things you have to keep in mind.
Of course in your case you will want to click a button that grabs all the ids and sends them to the action to update the records. One way would be to have javascript inject the id into an array when a check box is marked, then when the user clicks on the submit button the array is sent as params to the action that then loops through the arrays and updates the risks.
I'm sure there are better ways though, that's just the first thing that came to mind.
Your best bet would be to place all of the check boxes inside of a form.
<%= form_tag controller_method_name_your_controller_path, :method => 'get', do %>
...
<%= submit_tag 'submit' %>
<% end %>
Declare the path in the routes.rb file under your_controller
get :controller_method_name
You can group you check boxes together by using check_box:
<%= check_box 'mergerisks', risk.id %>
Then using params[:mergerisks] in the controller will return a hash with risk.id as the keys and a 1 or 0 depending on if the check box is checked.
Some documentation on routes and forms:
http://guides.rubyonrails.org/routing.html
http://guides.rubyonrails.org/form_helpers.html

Categories

Resources