How to select all from multiple kaminari pages - javascript

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

Related

Dynamically insert content for a full text user search with JS and Rails

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

Django: How to refresh table after filtering without refreshing the whole page?

The root of the issue is how to refresh the filtered table dynamically, without refreshing the whole page.
I am almost novice in python/html/css, so please make some comments like for a newbie [Thanks].
After some research on StackOverFlow, I found that it could be made with js, but I have almost no experience with js and I don't know how to use it in Django.
Is there any possibility using only Django tools? And how efficient it would be?
Maybe you can provide some examples of resolving the issue.
Here is the model:
class Player(models.Model):
last_name = models.CharField(
null=True,
blank=True,
max_length=255,
verbose_name="прізвище"
)
first_name = models.CharField(
null=True,
blank=True,
max_length=255,
verbose_name="ім'я"
)
city = models.ForeignKey(
City,
on_delete=models.SET_NULL,
null=True,
blank=True,
verbose_name="місто"
)
rating = models.PositiveIntegerField(
null=True,
blank=True,
verbose_name="рейтинг"
)
rank = models.ForeignKey(
Rank,
on_delete=models.SET_NULL,
null=True,
blank=True,
verbose_name="ранг"
)
local_rank = models.ForeignKey(
LocalRank,
null=True,
blank=True,
on_delete=models.SET_NULL,
verbose_name="розряд"
)
def __str__(self):
if self.last_name and self.first_name:
return self.last_name + ' ' + self.first_name
elif self.egd_last_name and self.egd_first_name:
return self.egd_last_name + ' ' + self.egd_first_name
else:
return self.id
I am using django-tables2 to render the table:
class PlayerTable(tables.Table):
full_name = tables.LinkColumn(
accessor="__str__",
verbose_name="Прізвище та ім'я",
order_by="last_name",
viewname='UGD:player_info',
empty_values=(),
args=[A('pk')]
)
local_rank = tables.Column(
accessor="local_rank.abbreviate",
order_by="id"
)
ufgo_member = tables.BooleanColumn(
verbose_name="Член УФГО"
)
class Meta:
model = Player
fields = (
'id',
'full_name',
'city',
'rating',
'rank',
'local_rank',
'ufgo_member'
)
attrs = {'class': 'main'}
I am using django-filter to make a filter form:
class PlayersFilter(django_filters.FilterSet):
last_name = django_filters.CharFilter(
lookup_expr='contains',
label="Прізвище"
)
first_name = django_filters.CharFilter(
lookup_expr='contains',
label="Ім'я"
)
city = django_filters.ChoiceFilter(
choices=[(city.id, city.name) for city in City.objects.all()],
empty_label="--Не обрано--",
label="Місто"
)
ufgo_member = django_filters.ChoiceFilter(
choices=[
(False, 'Ні'),
(True, 'Так')
],
name="ufgo_member",
label="Член УФГО",
)
class Meta:
model = Player
fields = (
'last_name',
'first_name'
)
The following is the issue - view / template.
I am using SingleTableMixin and FilterView in my view to take care of table and filter:
class RatingListView(SingleTableMixin, FilterView):
table_class = PlayerTable
table_pagination = False
template_name = 'UGD/rating_list.html'
filterset_class = PlayersFilter
Here I have an idea to divide the view on several parts, but I still don't know how to do it.
Maybe you have some suggestions to make it better?
My template:
<body>
<div class="filter">
<form id="filter_submit" class="filter">
{% block content %}
<div class="filter">
<table class="filter">
{{ filter.form.as_table }}
</table>
<button id="filter_submit_button" type="submit">OK</button>
</div>
{% endblock %}
</form>
</div>
<div>
{% render_table table %}
</div>
</body>
I think I should add some script, but I don't know how to use js yet.
The issue: after chosing filters and pressing OK, the whole page is refreshed with new data
And I want that only table was refreshed.
Please, give me a hint on how to do it.
Thank you very much.
Looking at the django-filter documentation (https://django-filter.readthedocs.io/en/develop/index.html), this app does the filtering in the back-end. I didn't see any javascript code executed to do this and it handles the filtering logic in the views. So that's why the app needs to refresh the page to show you the filtered table as a result.
I'm going to recommend you to implement the filtering functionality with javascript. You need to identify the elements of the table that you want to filter, the trigger elements (dropdowns and search boxes, as in django-filter) and link both of them to make modifications to the DOM. This way, you can have a dynamic table filter.
These couple of links can get you started with this:
http://www.w3schools.com/howto/howto_js_filter_table.asp
http://codepen.io/abocati/pen/vdKce [1]
[1]
JS
(function(document) {
'use strict';
var LightTableFilter = (function(Arr) {
var _input;
var _select;
function _onInputEvent(e) {
_input = e.target;
var tables = document.getElementsByClassName(_input.getAttribute('data-table'));
Arr.forEach.call(tables, function(table) {
Arr.forEach.call(table.tBodies, function(tbody) {
Arr.forEach.call(tbody.rows, _filter);
});
});
}
function _onSelectEvent(e) {
_select = e.target;
var tables = document.getElementsByClassName(_select.getAttribute('data-table'));
Arr.forEach.call(tables, function(table) {
Arr.forEach.call(table.tBodies, function(tbody) {
Arr.forEach.call(tbody.rows, _filterSelect);
});
});
}
function _filter(row) {
var text = row.textContent.toLowerCase(), val = _input.value.toLowerCase();
row.style.display = text.indexOf(val) === -1 ? 'none' : 'table-row';
}
function _filterSelect(row) {
var text_select = row.textContent.toLowerCase(), val_select = _select.options[_select.selectedIndex].value.toLowerCase();
row.style.display = text_select.indexOf(val_select) === -1 ? 'none' : 'table-row';
}
return {
init: function() {
var inputs = document.getElementsByClassName('light-table-filter');
var selects = document.getElementsByClassName('select-table-filter');
Arr.forEach.call(inputs, function(input) {
input.oninput = _onInputEvent;
});
Arr.forEach.call(selects, function(select) {
select.onchange = _onSelectEvent;
});
}
};
})(Array.prototype);
document.addEventListener('readystatechange', function() {
if (document.readyState === 'complete') {
LightTableFilter.init();
}
});
})(document);

Call a javascript function dynamically from controller

I am using jquery autocomple mechanism. It needs an array on which this javascript will run and show the result on typing.Thats why I need the availableTags array. But problem is that javascript is loading only the time of page loading and it is capturing the initial array but I need the javascript to be called dynamically during the type of each keyword typing...so how can I resolve this?
Actually I want to call this javascript function from my controller function.
My controller code is:
class EmployeesController < ApplicationController
skip_before_action :verify_authenticity_token
def new
#emp=Employee.new
#emps=Employee.all
#check=params[:pt]
puts #check
x_dynamic=check_ultimate(#check)
puts "return value"
puts x_dynamic
y=string_include(x_dynamic,#emps)
puts y
end
def string_include(substring,data_ref)
loo=0
#firstname_array=Array.new
data_ref.each do |d_ref|
puts "in the loop of string_include method"
my_string=d_ref.compact_string
if my_string.include? substring
puts "String "+ my_string+" includes "+substring
loo=loo+1
puts loo
puts d_ref.firstname
#firstname_array && #firstname_array.push(d_ref.firstname)
end
end
puts "This is the array"
puts #firstname_array
return 1
end
def create
#emp=Employee.new(u_params)
ret_val=string_check(params[:employee][:firstname])
if ret_val == 1
ret_one=convert(params[:employee][:firstname],#emp)
if ret_one == 1
#emp.save
redirect_to new_employee_path , :notice => "Inserted!!!"
end
elsif ret_val == 0
redirect_to new_employee_path , :notice => "Match,Not inserted!!!"
else
redirect_to new_employee_path , :notice => ret_val.to_s
end
end
def u_params
params.require(:employee).permit(:firstname)
end
def convert(entered_name,ref)
count =0
txt=entered_name.chomp
size1=txt.to_s.length
arr1=Array.new
for j in 0..size1
arr1 && arr1.push(txt && txt[j])
end
for i in 0..size1
if arr1
if arr1[i]==" " || arr1[i]=="#" || arr1[i]=="#" || arr1[i]=="{" || arr1[i]=="}" || arr1[i]=="(" || arr1[i]==")" || arr1[i]=="[" || arr1[i]=="]" || arr1[i]=="." || arr1[i]==";" || arr1[i]=="," || arr1[i]=="%" || arr1[i]=="&" || arr1[i]=="*" || arr1[i]=="!" || arr1[i]=="?" || arr1[i]=="$" || arr1[i]=="^" || arr1[i]==":" || arr1[i]=="-" || arr1[i]=="/"
count=count+1
arr1 && arr1[i]=""
end
end
end
temp=arr1 && arr1.join
final1=temp.gsub(/\s+/, "").downcase
ref.compact_string=final1
ref.save
return 1
end
def string_check(first_name)
count =0;
temp1=first_name
supplied_val=temp1
flag=100
puts "Entered in string_check method"
#employees=Employee.all
t1=Employee.all.count
puts t1
#employees.each do |employee|
puts "in the loop"
temp2=employee
table_val=temp2.compact_string
size1=supplied_val.to_s.length
# size2=table_val.to_s.length
puts table_val
puts supplied_val
arr1=Array.new
for k in 0..size1
arr1 && arr1.push(supplied_val && supplied_val[k])
end
for i in 0..size1
if arr1
if arr1[i]==" " || arr1[i]=="#" || arr1[i]=="#" || arr1[i]=="{" || arr1[i]=="}" || arr1[i]=="(" || arr1[i]==")" || arr1[i]=="[" || arr1[i]=="]" || arr1[i]=="." || arr1[i]==";" || arr1[i]=="," || arr1[i]=="%" || arr1[i]=="&" || arr1[i]=="*" || arr1[i]=="!" || arr1[i]=="?" || arr1[i]=="$" || arr1[i]=="^" || arr1[i]==":" || arr1[i]=="-" || arr1[i]=="/"
count=count+1
# puts count
arr1 && arr1[i]=""
end
end
end
temp_st=arr1 && arr1.join
final_st=temp_st.gsub(/\s+/, "").downcase
if final_st==table_val
puts "Entered in if"
flag=0
break
else
puts "Entered in else"
flag=1
end
end
puts #employees.count
return flag
end
def check_ultimate(get_val)
puts "Entered in check_ultimate method"
puts get_val
remove_data=get_val.to_s.chomp
#puts remove_data
si1=remove_data.to_s.length
puts si1
arr3=Array.new
for p in 0..si1
arr3 && arr3.push(remove_data && remove_data[p])
end
puts arr3
puts "array end,,,,,,,,,,,,,,,,,,,,,,,,,,,,"
count=0
for i in 0..si1
puts i
if arr3
#if arr3[i]=='#'
if arr3[i]==" " || arr3[i]=="#" || arr3[i]=="#" || arr3[i]=="{" || arr3[i]=="}" || arr3[i]=="(" || arr3[i]==")" || arr3[i]=="[" || arr3[i]=="]" || arr3[i]=="." || arr3[i]==";" || arr3[i]=="," || arr3[i]=="%" || arr3[i]=="&" || arr3[i]=="*" || arr3[i]=="!" || arr3[i]=="?" || arr3[i]=="$" || arr3[i]=="^" || arr3[i]==":" || arr3[i]=="-" || arr3[i]=="/"
count=count+1
puts count
arr3 && arr3[i]=""
end
end
end
temp_st_two=arr3 && arr3.join
final_st_two=temp_st_two.gsub(/\s+/, "").downcase
puts final_st_two
puts "..............the ajax string"
return final_st_two
end
end
My view file is:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>jQuery UI Autocomplete - Default functionality</title>
<link rel="stylesheet" href="//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css">
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="//code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<link rel="stylesheet" href="/resources/demos/style.css">
<script>
$(document).ready(function(){
$("input").keyup(function(){
var box = document.getElementById("tags");
document.getElementById("abc").innerHTML = box.value;
$.getScript('/employees/new?pt=' + encodeURIComponent(box.value))
});
});
$(function() {
var availableTags=<%= #firstname_array.inspect.html_safe %>;
availableTags.toString();
document.getElementById("aaa").innerHTML = availableTags;
// document.getElementById("abc").innerHTML = availableTags;
$( "#tags" ).autocomplete({
source: availableTags
});
});
</script>
</head>
<body>
<div class="ui-widget">
<hr>
<%= form_for #emp do |f| %>
Firstname: <%= f.text_field :firstname, id: 'tags' %></br></br>
<%= f.submit %>
<%end%>
<hr>
<h5><%=#msg%></h5>
<h3>Display all employee information</h3></br>
<% if !#emps.blank? %>
<% for item in #emps %>
<%= item.firstname %> </br>
<%end%>
<%else%>
<%end%>
</div>
<div id="abc"></div>
<div id="aaa"></div>
</body>
</html>

Cascading Select Boxes in Admin

I have the following code to implement a cascading select boxes (as the field contract_mod is OneToOneField I can't use django-smart-selects or django-ajax-select).
When I'm creating a fieldset with the fields that I want to be shown, I have to put the contracts_from_selected in order to see the results in the admin interface (since contract_mod remain disabled after applying the code.).
fieldsets = [
[ None,
{
"fields" : [
("contracts_from_selected")
]
}
]
So I guess I should copy to another field the value of contracts_from_selected in order to be used in the fieldset.
Any suggestion?
models
class Person(models.Model):
name = models.CharField(max_length=20)
def __unicode__(self):
return self.name
def get_name(self):
return self.name
class Contract(models.Model):
person = models.ForeignKey(Person) #person hired
contract_mod = models.OneToOneField('self', blank = True, null = True)
contract_name = models.CharField(max_length=20) #just for testing
def __unicode__(self):
return self.get_name() + " " +self.contract_name
def get_name(self):
return self.person.get_name() #to make sure you get the person name in the admin
def contract_mod_name(self):
if self.contract_mod:
return self.contract_mod.contract_name
else:
return ""
admin
class SelectField(forms.ChoiceField):
def clean(self, value):
return value
class ContractForm(forms.ModelForm):
contracts_from_selected = SelectField()
class Meta:
model = Contract
widgets = { 'contract_mod' : forms.widgets.Select(attrs={'hidden' : 'true'}) }
class ContractAdmin(admin.ModelAdmin):
form = CForm
list_display = ('contract_name','get_name','contract_mod_name')#what you like
def save_model(self, request, obj, form, change):
if request.POST.get('contracts_from_selected'):
obj.contract_mod=Contract.objects.get(id=int(request.POST.get('contracts_from_selected')))
obj.save()
change_form
$(function () {
$("#id_person").change(function () {
var options = $("#id_contract_mod option").filter(function () {
return $(this).html().split(" ")[0] === $("#id_person option:selected").html();
}).clone();
$("#id_contracts_from_selected").empty();
$("#id_contracts_from_selected").append(options);
});
});

Dynamic select menu's in Rails?

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

Categories

Resources