Rails nested form dynamic div id - javascript

In my Rails App I am using a nested form and in my controller I have a loop which looks like this:
2.times do
practice = #team.practices.build
end
In my nested form I create a select field with the following code:
<%= f.select :day, Practice.days.keys.to_a, {}, {class: 'form-control'} %>
which gets the id: "team_practices_attributes_0_day". This is for the first practice. The second practice shower receives this id: "team_practices_attributes_1_day"
Now I want to use the variable (0, 1) in my other ids for some jQuery. Is there a way to access this number in a form?

I'm a little confused by the context of your 2.times loop (is practice referenced on the form?) Is that where the two selects are built?). So, I'm not sure if you want this on the select helper or in the answers. But a data attribute is an easy way to access data from any element.
You can add data attributes to the a select helper as part of the HTML options.
<%= f.select :day, Practice.days.keys.to_a, {}, {class: 'form-control', data-id: practice.id} %>
And then access them by calling the attribute name after data in jQuery. Note, you might want to add a specific class to query against.
var id = $('.form-control').data("id");

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.

Autocompletion in a nested form text field

I am using rails 5 and i want to autocomplete a text field which is in a nested form. The nested form has been generated via cocoon.
When i try to use auto complete function in the parent form it works fine. However it seems like my javascript doesnt work for the nested form.
This is model file of the nested form
class Programline < ApplicationRecord
belongs_to :program, inverse_of: :programlines
belongs_to :exercise
def exercise_name
exercise.try(:exe_desc)
end
def exercise_name=(exe_desc)
self.exercise = Exercise.find_by_exe_desc(exe_desc) if
exe_desc.present?
end
end
This is my text field within the nested form.
<%= f.text_field :exercise_name, data: {autocomplete_source: Exercise.order(:exe_desc).map(&:exe_desc)} ,class: "form-control", placeholder: "Exercise", autofocus: true %>
This is my javascript file.
jQuery
$(function() {
$(document).on("focus","programline_exercise_name", function() {
$('programline_exercise_name').autocomplete({
source: $('programline_exercise_name').data('autocomplete- source')
});
});
})
When i fill the form it works fine, inserts data and builds relationship without any problem. But it does not automatically find the data which is in exercise table.
Thanks for your helps.
You cross-posted this in cocoon issues, so I will replicate my answer to you here.
Your selection filter for jquery seems wrong. You write on("focus","programline_exercise_name" and $('programline_exercise_name') which would go looking for a html-element with that name. Like $('p') will select all paragraphs. I am assuming you want to select an element with id or class programline_exercise_name ? For dynamically added fields you would definitely be using a class, but then your jquery snippet would also be wrong.
So if you add the wanted class-name to the field
<%= f.text_field :exercise_name, data: { autocomplete_source: Exercise.order(:exe_desc).map(&:exe_desc)},
class: "form-control programline_exercise_name",
placeholder: "Exercise",
autofocus: true %>
You would then need something like
$(document).on("focus", ".programline_exercise_name", function() {
var _this = $(this);
_this.autocomplete({
source: _this.data('autocomplete- source')
});
});
Not tested, so let me explain: when the focus event is triggered on any of the text-fields with class .programline_exercise_name, only set the autocomplete for the element that caused the event.
Your JQuery should look as follows (assuming your nested field has id programline_exercise_name) :
$(function() {
$(document).on("focus","#programline_exercise_name", function() {
$('#programline_exercise_name').autocomplete({
source: $('#programline_exercise_name').data('autocomplete- source')
});
});
});

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 make a change event only run one time the event is called for a class

I'm designing a multi dynamic select menus, i.e., I have a menu for brands, after the user select the brand, using JavaScript and AJAX, I will search for the models available from that brand and add them to the second select menu. This process repeats again but this time showing the features of the model selected.
To do this, and because I have many different areas that need the same system, I use a class with the same name in every brand select menu and another one to every model select menu.
<div class='brand_select' id='14'>
<%= f.collection_select :brand, Product.find_all_by_area(14, :group => 'brand'), :brand, :brand, :prompt => 'Choose brand' %>
</div>
<div class='model_select'>
<%= f.collection_select :model, Product.find_all_by_area(14), :model, :model, :prompt => 'Choose model' %>
</div>
<div class='brand_select' id='15'>
<%= f.collection_select :brand, Product.find_all_by_area(15, :group => 'brand'), :brand, :brand, :prompt => 'Choose brand' %>
</div>
<div class='model_select'>
<%= f.collection_select :model, Product.find_all_by_area(15), :model, :model, :prompt => 'Choose model' %>
</div>
And the JavaScript:
$('.brand_select').change(function(event) {
// option selected
var brand=$(event.target).find('option:selected').val();
// if none is selected
if (brand == ''){
$(event.target).parent().parent().find('.modelo_select').hide();
$(event.target).parent().parent().find('.caracteristica').hide();
}
else {
$(event.target).parent().parent().find('.modelo_select').show();
// find id to search on the database
var id=$(event.target).parent().attr('id');
// find the target (id of the object)
var target=$(event.target).attr('id');
$.ajax({
type: "POST",
url: "http://"+location.host+"/model/"+brand+"/"+id+"/"+target,
brand: brand,
id: id,
target: target
});
}
});
$('.model_select').change(function(event) {
// find model selected to search on the database
var model=$(event.target).find('option:selected').val();
// find brand selected to search on the database
var brand=$(event.target).parent().parent().find('.marca_select').find('option:selected').val();
// find id to search on the database
var id=$(event.target).parent().parent().find('.marca_select').attr('id');
// find the target (id of the object)
var target=$(event.target).attr('id');
$.ajax({
type: "POST",
url: "http://"+location.host+"/feature/"+brand+"/"+model+"/"+id+"/"+target,
brand: brand,
model: model,
id: id,
target: target
});
});
This code works but it repeats the event change the same number of times as the classes with that name.
What I want to do is for the function to run only one time every time a change event is called for the class.
I don't know if this is possible with class structure that I have or if I have to associate an id or a class with different names for each area to the function.
I don't see why the event should fire twice because all you're doing with $(selector).change is saying that every time a change event fires on something with that selector you want to handle it. I even ran a quick test to be sure and it doesn't fire more than once.
Can you explain a bit better what the symptom actually is? As in, what actually happens twice? Does everything in your event handler happens twice?
I was thinking that your selectors for the actions you perform on the parents might be a bit too lax ($(event.target).parent().parent()) so if you only want to do something on the container where your event was fired that wouldn't be the best way (but then again I don't know what your end purpose is here).
That for your help, I found out that the problem has nothing to do with Javascript but is instead on the Ruby on Rails.
I was adding on application.html.erb other js files and if you have the //= require_tree on the application.js it adds every js file in the tree, so adding js files on application.html.erb will make them repeat and cause strange behaviors like this one.

Click rails button to permanently change data model and css

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!

Categories

Resources