I am trying to get dynamic select menu's working in my Rails application.
I have a model called kase, a model called person and a model called company.
When a user creates a new kase, they are presented with a select field to choose a Person and a select field to choose a Company.
I am trying to make it dynamic, so if they choose company a in the first select field then only employees of company a will be listed in the Person field.
The model associations are as follows:
class Kase < ActiveRecord::Base
belongs_to :company # foreign key: company_id
belongs_to :person # foreign key in join table
belongs_to :surveyor,
:class_name => "Company",
:foreign_key => "appointedsurveyor_id"
belongs_to :surveyorperson,
:class_name => "Person",
:foreign_key => "surveyorperson_id"
-------------------------------------------------
class Company < ActiveRecord::Base
has_many :kases
has_many :people
def to_s; companyname; end
-------------------------------------------------
class Person < ActiveRecord::Base
has_many :kases # foreign key in join table
belongs_to :company
UPDATED JAVASCRIPT
var people = new Array();
<% for person in #people -%>
people.push(new Array(<%= person.company_id %>, '<%=h person.personname %>', <%= person.id %>));
<% end -%>
function personSelected() {
alert('hello world');
appointedsurveyor_id = $('kase_appointedsurveyor_id').getValue();
options = $('kase_surveyorperson_id').options;
options.length = 1;
people.each(function(person) {
if (person[0] == appointedsurveyor_id) {
options[options.length] = new Option(person[0], person[1]);
alert('hello world')
}
});
if (options.length == 1) {
$('kase_surveyorperson_id').hide();
} else {
$('kase_surveyorperson_id').show();
}
}
document.observe('dom:loaded', function() {
$('kase_appointedsurveyor_id').observe('change', personSelected);
});
var people = new Array();
<% for person in #people -%>
people.push(new Array(<%= person.id %>, '<%=h person.login %>'));
<% end -%>
function personSelected() {
alert('hello world');
appointedsurveyor_id = $('company_appointedsurveyor_id').getValue();
options = $('person_appointedsurveyorperson_id').options;
options.length = 1;
people.each(function(person) {
if (person[0] == appointedsurveyor_id) {
options[options.length] = new Option(person[0], person[1]);
}
});
if (options.length == 1) {
$('person_field').hide();
} else {
$('person_field').show();
}
}
document.observe('dom:loaded', function() {
//companySelected(); << remove this
$('person_field').observe('change', personSelected);
});
Try observe_field. http://api.rubyonrails.org/classes/ActionView/Helpers/PrototypeHelper.html#M002183
Related
I am creating sort of an api app with node.js, express, body-parser, ejs, fs. I have the server running and everything else working. The problems is that have an object(JSON file) that is parsed, but I can't get access to certain attributes when using my index.ejs template:
{
"x":{
"color": "black"
"total": 1
},
"y":{
"color": "red"
"total": 5
}
}
My javascrip file has the following function that loops trough the file.
var keys = Object.keys(Obj);
for (var i = 0; i< keys.length; i++){
var k = keys[i];
var colorKey = Obj[k].color;
var totalKey = Obj[k].total;
}
var db = keys;
res.render("index", {
db: db
}
Then on my index.ejs I have the code that works with the first attribute ("x" and "y") but can't access the rest.
Welcome to the main page!
<% for (var i = 0; i < db.length; i++) { %> ////This is the for loop that shows "x" and "y"
<h1> We have the variable: <%= db[i] %> </h1>
<h1> The color is: <%= db[i].color %></h1>
<h1> There is <%= db[i].total %> left!</h1>
<% } %>
The page shows both variables 's but shows nothing when it comes to color and total. Is there a way to loop those in ejs?
I presume you don't actually need the preprocessing. Instead, you can do can make an array of keys
db = /* the source object */
const keys = Object.keys(db)
res.render("index", { db })
<% for (let i = 0; i < keys.length; i++) { %>
<h1> We have the variable: <%= keys[i] %> </h1>
<h1> The color is: <%= db[keys[i]].color %></h1>
<h1> There is <%= db[keys[i]].total %> left!</h1>
<% } %>
I have a search input-field and I want to display content dynamically depending on what the User types in it.
The Database hast 1,5k slots and I can allready search by giving a parameter
?search_for=SOMETHING_TO_SEARCH_FOR
where SOMETHING_TO_SEARCH_FOR is just a string. It does a full text search over the database and gives me the results.
I would like to replace the search results with the current shown elements inside
<div class = 'slots-container' id = 'dynamic'>...</div>
index.html.erb
<div class = "index-body">
<%= link_to 'home', root_path, id: 'home', hidden: true %>
<div class = "search_bar" id = 'search'>
<input type="text" placeholder="Search..">
</div>
<div id = 'slots'>
<div class = 'slots-container' id = 'dynamic'>
<%= render #slots %>
</div>
</div>
<%= will_paginate #slots, hidden: true %>
</div>
index.js.erb
appends the next elements to the div with the id: dynamic and removes the .pagination div which is included by will_paginate statement if we have no more pages so that my script for loading more pages gets a null reference
$('#dynamic').append('<%= j render #slots %>');
<% unless #slots.next_page %>
$('.pagination').remove();
<% end %>
main.js
Loads a new page if User is only 100px away from the end of the document,
with the url: next_page from the link with the .next class
var ready = true;
$( document ).on('turbolinks:load', function() {
$("img").lazyload();
document.onscroll = function(){loadNextPage()};
})
function loadNextPage(){
var window_top = $(window).scrollTop();
var doc_height = $(document).height();
var window_height = $(window).height();
var window_bottom = window_top + window_height;
var should_scroll = doc_height - window_bottom < 100;
var next_page = $('.next_page').attr('href');
if (ready && should_scroll && next_page) {
ready = false;
$.getScript(next_page).done(function() {
$("img").lazyload();
ready = true;
});
}
}
function loadSearch(){
var root_page = $('#home').attr('href');
...
}
As you see my loadSearch() function should set the param search_for and append it to my root_page url and my index.js.erb should replace the current content of the document with the search results and also being able to still do infinite scrolling feature.
I think the controller is of no interest jsut be sure that #slots has the right paginated slot elements that needs to be drawn
I solved it now as follows:
index.html.erb
<div class = "index-body">
<%= link_to 'home', root_path, id: 'home', hidden: true %>
<div class = "search-bar">
<input class = "search-input" type="text"
placeholder="Search..."
id = "search-input">
</div>
<div id = 'slots-container'>
<div class = 'slots-container' id = 'slots'>
<%= render #slots %>
<%= will_paginate #slots, hidden: true,id: "paginate" %>
</div>
</div>
</div>
index.js.erb
#identical_search indicates that last search result is the same as the current one so dont do anything
<% unless #identical_search %>
if (dirty) {
dirty = false;
$('#slots').empty();
}
$('#slots').append('<%= j render #slots %>');
<% if #slots.next_page %>
if(document.getElementById("paginate") === null)
$('#slots').append('<%= j will_paginate #slots, hidden: true, id: "paginate" %>');
$('#paginate').replaceWith('<%= j will_paginate #slots, hidden: true, id: "paginate" %>');
<% else %>
$('#paginate').remove();
<% end %>
<% end %>
main.js
var documentLoaded = false;
var dirty = false;
var pending = false;
var loadSearchTimer = null;
$( document ).on('turbolinks:load', function() {
...
documentLoaded = true;
document.getElementById("search-input").addEventListener("input", startTimerForUserInput);
})
...
function startTimerForUserInput(){
if (loadSearchTimer !==null)
clearTimeout(loadSearchTimer);
loadSearchTimer = setTimeout(loadSearch, 300);
}
function loadSearch(){
if (documentLoaded){
documentLoaded = false;
dirty=true;
var search_input = document.getElementById("search-input");
var search_for = search_input.value;
var root_page = $('#home').attr('href');
if(search_for && search_for !== '')
root_page += "?search_for=" + search_for;
$.getScript(root_page).done(function() {
$("img").lazyload();
documentLoaded = true;
if(pending){
pending = false;
loadSearch();
}
});
}
else
pending = true;
}
In main.js I use a Timer to only search 300 ms after last user input to limit the calls so that it doesnt search after every input. When the getScript returns success I am checking if a search request was submitted while the current request was processed to send a new request via pending variable.
dirty is set to true when the user search so that the index.js.erb knows to empty out the div containing the elements.
This runs smooth and I like it how it is.
I hope this will help someone in the future...
If it helps to udnerstand the whole picture, here is the controller:
class SlotsController < ApplicationController
require 'will_paginate/array'
##last_search_content = nil
def index
#identical_search = false
#hashtags = Hashtag.all
if params[:search_for]
search_str = params[:search_for].gsub(/\s\s+/, ' ').strip
#slots = Slot.where("LOWER(slot_name) LIKE LOWER('%#{search_str}%') ")
if ##last_search_content.to_set == #slots.to_set && (params[:page] == nil || params[:page] == 1)
#identical_search = true
end
elsif params[:hashtags]
#slots = slotsWithAtLeastOneOfThose(params[:hashtags])
else
#slots = Slot.all
end
##last_search_content = #slots
unless #identical_search
#slots = #slots.paginate(page: params[:page])
end
respond_to do |format|
format.html
format.js
end
end
def show
#slot = Slot.find(params[:id])
end
private
def slotsWithAtLeastOneOfThose(hashtags)
slots=[]
hashtags.split(' ').each do |h|
slots += Slot.joins(:hashtags).where("hashtags.value LIKE ?", "%#{h}%")
end
return slots.uniq
end
end
I have 3 models Dealer, Label, Number
The relations between models
dealer and label - habtm,
dealr and number - has_many through labels,
label and number - has many.
my first drop down to select dealers, based on that 2nd drop down has to display the labels
f.input :dealer, :input_html => {
:onchange => "
var dealer = $(this).val();
$('#contact_number_id').val(0).find('option').each(function(){
var $option = $(this),
isCorrectDealer = ($option.attr('data-dealer') === dealer);
$option.prop('hidden',!isCorrectDealer);
});
"
}
f.input :label, collection: Label.all.map{
|v| [v.label_name,v.id, {"data-dealer" => v.dealer_id}]
}
It is not working for me...Please help me with this
I want to call a JS function when a button in my Rails form is clicked. The function is defined in the .js.erb file found below.
When I click the button, Chrome's JS console throws the following error:
Uncaught ReferenceError: logTime is not defined
I know this means it can't find the function, but I don't see why. Especially since I added <%= javascript_include_tag "track.js.erb" %> to the file. Any ideas?
Apologies up-front, but my Google-fu yielded no results.
_track_time_form.html.erb
<div id="countdown-timer"></div>
<div id="playback-button">►</div>
<div id="track-time-form">
<%= form_for #project, :url => { :action => "log_time" }, remote: true do |p| %>
<ul>
<li><%= p.label :project, "Project:"%><br>
<%= p.collection_select(:id, current_user.projects, :id, :name) %></li>
<%= p.hidden_field :time_logged, :value => 0 %> <!-- value set by script in log_time.js.erb -->
<li><%= p.submit "Log time", id: "log-time-button", :onclick => "logTime()" %></li>
</ul>
<% end %>
</div>
The function the handler is calling can be found here:
track.js.erb
//initialise form
timeTrackingForm = window.open("", "", "height=700,width=500");
$(timeTrackingForm.document.body).html("<%= j render( :partial => 'track_time_form' ) %>");
//assign variables
var timer = $("#countdown-timer", $(timeTrackingForm.document));
var playbackControls = $("#playback-button", $(timeTrackingForm.document));
var form = $("#track-time-form", $(timeTrackingForm.document));
var formUl = $("#track-time-form ul", $(timeTrackingForm.document));
var formLi = $("#track-time-form li", $(timeTrackingForm.document));
var logTimeButton = $("#log-time-button", $(timeTrackingForm.document));
var timerPaused;
//initialise timer
$(timeTrackingForm.document).ready(function(){
initialiseTimer();
style();
$(playbackControls).click(function() {
playOrPause();
});
});
function initialiseTimer() {
$(timer).timer({
format: '%H:%M:%S'
});
$(timer).timer('pause');
timerPaused = true;
}
function style() {
$(timer).css({'color':'black','font-size':'50px', 'margin':'auto', 'width':'180px'});
$(playbackControls).css({'color':'#290052', 'font-size':'50px', 'margin':'auto', 'width':'55px'});
$(form).css({'width':'300px','margin':'auto'});
$(formUl).css({'list-style-type':'none'});
$(formLi).css({'margin':'0 0 25px 0','font-sizeL':'18px','font-family':'Arial'});
$(logTimeButton).css({'width':'180px','font-size':'18px','background-color':'green','color':'white','margin-top':'15px'});
}
function playOrPause() {
if (timerPaused == true) {
$(timer).timer('resume');
timerPaused = false;
}
else {
$(timer).timer('pause')
timerPaused = true;
}
}
function logTime() {
$(timer).timer('pause');
var secondsTracked = $(timer).data('seconds');
$('input:hidden').val(secondsTracked);
$('#countdown-timer').timer('reset');
}
For any future readers interested in the solution, here's how I got around the issue (huge thanks to the helpful commenters above).
I added an event listener to the form in track.js.erb, which is triggered when the button is clicked (but before the form actually submits and sends data to the controller). Here it is:
var actual_form = $("#new_project", $(timeTrackingForm.document));
$(actual_form).submit(function(){
$(timer).timer('pause');
timerPaused = true;
var secondsTracked = $(timer).data('seconds');
$(hidden).val(secondsTracked);
resetTimerDisplay();
});
I am using Kaminari for paging in my rails application. I need to select all for items on the current page or every page. The current page is rather simple bundle how can I select all the items on other pages.
file_items_controller.rb
def index
account = Account.includes(:credentials).find params[:account_id]
#page = page_param.to_i
#tab = 'Files'
#sort_by = sort_by
#credential = if params[:credential_id].blank?
account.credentials.first || nil
else
account.credentials.find(params[:credential_id])
end
return unless #credential
file_items = FileItem.file_item_list(#credential.root_folder, sort_by)
#total_count = file_items.count
#max_per_page = file_items.count <= 15 ? 'all' : max_per_page.to_i
file_items_scope = Kaminari.paginate_array(file_items.select(&:file?), total_count: file_items.count).page(page_param)
#file_items = if max_per_page == 'all'
file_items_scope.per(file_items.count) || []
else
file_items_scope.per(max_per_page) || []
end
end
file_items.js
$('a.select-all-current').click(function(e){
e.preventDefault();
$('input:not(:checked)').each(function(_, element){
$(element).click();
});
});
$('a.select-all-files').click(function(e){
e.preventDefault();
$('a.select-all-current').click();
});
index.html.slim
...
.row
ul.file-list
- #file_items.each do |file_item|
li.row.file-item
.col-lg-9
p.label
= "#{file_item[:path]}/#{file_item[:name]}"
p.file-size
= "#{number_to_human_size(file_item[:size]).downcase} | "
.col-lg-1.pull-right
= check_box_tag "file_id_#{file_item[:id]}", file_item[:id], false, class: 'file-box'
...
This example assumes ActiveRecord but you should be able to adapt it to your situation:
class ItemsController < ApplicationController
def index
#items = if params[:per_page] == 'all'
Item.all
else
Item.page( params[:page] ).per( params[:per_page] )
end
end
end