I've noticed how Akira Matsuda have added helper in Kaminari gem and made ti a lot simple for us to use Load more button.
I've read the document and figured simply adding
<%= link_to_next_page #items, 'Next Page' %>
should make the ajax load button work, but id didn't.
So I googled few other articles for some help and I wrote
few extra things...
Here are what I have right now.
I have these files
root.html.slim
ul#works
= render :partial => "works"
work.html.slim
- #works.each do | work |
= link_to work
li
span.thumb
= work_image_of work
h4 = link_to work.title, work, thumb:true
p.pull-left
small
= work.collaborators.count.to_s
| collaborators
= link_to_next_page #works, 'Next Page', id: 'view_more'
index.controller
def root
#works = Work.page(params[:page]).per(9)
render :layout => 'application'
end
also added in application.js
$('#works').append("<%= escape_javascript(render 'works', object: #works) %>");
$("#view_more").replaceWith("<%= escape_javascript(
link_to_next_page(#works, 'more', remote: true, id: 'view_more')
) %>");
and this in action helper,
def link_to_next_page(scope, name, options = {}, &block)
param_name = options.delete(:param_name) || Kaminari.config.param_name
link_to_unless scope.last_page?, name, {param_name => (scope.current_page + 1)}, options.merge(:rel => 'next') do
block.call if block
end
end
I have jquery included in my js files.
What am I missing?
Please help me out!
Thank you for your time!!
Related
Not sure if I am asking this question wrong, but I can't seem to find exactly the issue I am faced with.
I have a very basic rails 6 app for a task list. Tasks are either complete or incomplete and the change/update of this is to be done via javascript (the html side works just fine).
Here is my form partial _task.html.erb:
<%= form_with model: task, html: { class: "edit_task", id: "edit_task_#{task.id}" } do |f| %>
<%= f.check_box :complete %>
<%= f.submit "Update" %>
<%= f.label :complete, task.name %>
<%= link_to "(remove)", task, method: :delete, data: {confirm: "Are you sure?"}, remote: true %>
<% end %>
Here is the javascript to submit the form, tasks.js
function submit_update(){
const checkboxes = document.querySelectorAll('.edit_task input[type=checkbox]');
const submitbutton = document.querySelectorAll('.edit_task input[type=submit]');
submitbutton.forEach(button => button.remove());
checkboxes.forEach(checkbox => {
checkbox.addEventListener('click', () => checkbox.parentElement.submit());
});
}
window.addEventListener('turbolinks:load', event => {
submit_update();
document.addEventListener('task_added', event => submit_update());
});
This part works just fine, but once submitted and based on this section of the controller
def update
#task.update!(task_params)
respond_to do |format|
format.html { redirect_to root_url, notice: 'Task successfully updated' }
format.js
end
end
My understanding is together this should launch update.js.erb, which currently looks like
unction update_task() {
const current_task = document.querySelector('#edit_task_<%= #task.id %>');
const complete_tasks = document.querySelector('#complete_tasks');
const incomplete_tasks = document.querySelector('#incomplete_tasks');
<% if #task.complete? %>
complete_tasks.insertAdjacentHTML('beforeend',current_task.innerHTML);
<% else %>
incomplete_tasks.insertAdjacentHTML('beforeend',current_task.innerHTML);
<% end %>
}
update_task();
I have tried changing the above to a single line using an alert call and it still never gets called.
If someone could let me know why update.js.erb is not being called, it would be much appreciated :)
If any additional information is required, please let me know?
EDIT:
On further testing, I have found that if I submit the update via the click of the button, ie remove the submission via javascript, that the update.js.erb is actioned correctly.
So it would seem the focus needs to be on the tasks.js file and how that submits?
What is weird is that when that is included, after the submit the HTML format runs just fine, just not the js format??
After a number of days of trying to get the correct search words I have found the solution and happy to submit for others to find :)
The magic can be found here
The change for my example is as follows --
checkboxes.forEach(checkbox => {
checkbox.addEventListener('click', () => checkbox.parentElement.submit());
});
// becomes
checkboxes.forEach(checkbox => {
checkbox.addEventListener('click', event => {
checkbox.parentElement.dispatchEvent(new Event('submit', {bubbles: true}));
});
});
I have since edited the update.js.erb (does the same thing just a bit cleaner), but it also
needed an additional entry for newly added items to trigger having the entry change positions:
$ cat app/views/tasks/update.js.erb
function update_task() {
const current_task = document.querySelector('#edit_task_<%= #task.id %>');
const to_task = document.querySelector('#<%= #task.complete? ? "complete_tasks" : "incomplete_tasks" %>');
// Remove from previous list
current_task.remove();
// Append to the end of new list
to_task.insertAdjacentHTML('beforeend', '<%= j render(#task) %>');
// Advise tasks.js that new entry needs listener added
to_task.dispatchEvent(new Event('task_added', { bubbles: true }));
}
update_task();
Hope others find this useful :)
I got this menu on my website
%button.dropdown-button
.current-user{onclick:'showMenu()'}
%img.current-user-image{src:current_user.picture_url}
= current_user
%i.fa.fa-bars{'aria-hidden':true}
.dropdown-content
.menu-option{onclick:'showFilters()'}
Filter Transactions
%i.fa.fa-paper-plane{'aria-hidden':true}
.transaction-filters
.filter-option
My Transactions
%i.fa.fa-square-o
%a{href:'#'}
.menu-option
Balance History
%i.fa.fa-history{'aria-hidden':true}
%a{href:destroy_user_session_path}
.menu-option
Sign Out
%i.fa.fa-sign-out{'aria-hidden':true}
And I got this timeline with transactions
.timeline-container
- #transactions.each do |transaction|
.transaction-container
.transaction-header-container
.transaction-kudos-container
= "+#{transaction.amount}"
%span.currency
₭
.transaction-avatar-container
%div
= image_tag transaction.receiver_image, class:'avatar'
.transaction-body-container
.transaction-content
%span
= "#{transaction.sender.name}:"
%span.highlighted
= "+#{transaction.amount} ₭"
%span
to
%span.highlighted
= transaction.receiver_name_feed
%span
for
%span.highlighted
= transaction.activity_name_feed
%div
-#%button#like-button
-# Like
%span.post-time-stamp
= "#{distance_of_time_in_words(DateTime.now, transaction.created_at)} ago"
= paginate #transactions
They are both rendered on my index.html.haml
So when I click the div .filter-option.sent I want to change the code change from
- #transactions.each do |transaction|
to
- #all_transactions.each do |transaction|
to filter out the transactions of the current user without reloading the page.
These variables are defined in my controller
#transactions = Transaction.order('created_at desc').page(params[:page]).per(20)
#all_transactions = Transaction.all_for_user(current_user)
With in my model the method all_for_user
def self.all_for_user(user)
Transaction.where(sender: user).or(Transaction.where(receiver: user)).order('created_at desc').page.per(20)
end
I tried a lot of things with AJAX but nothing seems to please me. Somebody can help me?
So if you'd like to replace that #transactions list with AJAX you will need to do a few things..
1) Move the #transactions block into a partial that takes a local variable.
#transactions-wrapper
= render partial: 'transactions/list', locals: {transactions: #transactions}
2) Create a link that submits to a route, that hits a controller action as #js ('data-remote': true ) ( or write a javascript function that triggers $.ajax request: https://www.w3schools.com/jquery/ajax_ajax.asp
%a.transaction-filter{href: "/transactions/#{transaction_type}", 'data-remote': true}
or ex..
%a.study{href: "/transactions/recent", 'data-remote': true}
%a.study{href: "/transactions/past", 'data-remote': true}
3) define the route
get '/transactions/:type' => 'transactions#filter'
4) re-assign the variable based on the filter, and re-render that partial with the new data in a filter.js.erb file thats in the view directory -> app/views/transactions
def filter
#filtered_transactions = Transactions.where(type: params[:type] ).order('created_at DESC')
respond_to do |format|
format.html
format.js { render :action => 'filter.js.erb', :layout => false}
end
end
$("#transactions-wrapper").html("<%= j render(:partial => 'transactions/list'', :locals => { transactions: #filtered_transactions } ) %>");
alert('Transactions have been filtered, yo')
Let me know if that makes sense! Except please don't actually javascript alert at the end ;)
I have trying to follow this question but I am stuck.
In my controller I have:
def index
if params[:sort] == 'stars'
#projects = [Project.first]
else
#projects = Project.all
end
respond_to do |format|
format.html
format.js { render 'populate_projects', :formats => [:js] }
end
end
in routes:
get '/inspire/:sort' => 'projects#index'
in view:
= collection_select :project, :id, Project.all, :id, :name, {}, {:onchange => '$.get("/inspire/stars")'}
%div#normal
= render 'projects_list'
%div#stars{ style: 'display: none' }
my _projects_list.html.haml has:
%div
- #projects.each do |project|
%div
%p
#more code...
and finally in populate_projects.js.haml:
:plain
$("#stars").html("#{escape_javascript render(partial: 'projects/projects_list')}");
$("#normal").hide();
$("#stars").show();
Probably the program doesn't make sense as I am testing if ajax call is working. However, what should happen is when I change the state of dropdown an ajax call must be made which renders 'propulate.js.haml' and list of projects must change from all to just first, but is not. In my terminal I can see that call is being made but 'populate.js.haml' is never rendered. Can someone please help!
Make sure your Ajax call requests the JS format:
= collection_select :project, :id, Project.all, :id, :name, {}, {:onchange => '$.get("/inspire/stars.js")'}
If you want to make an ajax call you need to include:
"data-remote" => true
See here for more information
I'm trying to follow along with the instructions on how to implement jquery-tokeninput here:
How to use jquery-Tokeninput and Acts-as-taggable-on
I am trying to attach tags to a "post". I can use the text field in my form to create new tags. However, the javascript just will not fire at all. It's as if none of the code even exists. I don't get any error messages. The text field just acts as a normal text field with no javascript events firing. What can be the issue? Here is my relevant code:
post model
attr_accessible :tag_list
acts_as_taggable_on :tags
posts controller
def tags
query = params[:q]
if query[-1,1] == " "
query = query.gsub(" ", "")
Tag.find_or_create_by_name(query)
end
#Do the search in memory for better performance
#tags = ActsAsTaggableOn::Tag.all
#tags = #tags.select { |v| v.name =~ /#{query}/i }
respond_to do |format|
format.json{ render :json => #tags.map(&:attributes) }
end
end
routes
# It has to find the tags.json or in my case /products/tags.json
get "posts/tags" => "posts#tags", :as => :tags
application.js
$(function() {
$("#post_tags").tokenInput("/posts/tags.json", {
prePopulate: $("#post_tags").data("pre"),
preventDuplicates: true,
noResultsText: "No results, needs to be created.",
animateDropdown: false
});
});
Out of frustration I also created a posts.js.coffee file just to see if that was the problem:
$ ->
$("#post_tags").tokenInput "/posts/tags.json",
prePopulate: $("#post_tags").data("pre")
preventDuplicates: true
noResultsText: "No results, needs to be created."
animateDropdown: false
post form view
<%= f.label :tag_list %>
<%= f.text_field :tag_list, :id => "post_tags", "data-pre" => #post.tags.map(&:attributes).to_json %>
Am I missing something here? Any help would be greatly appreciated!!!
Thanks!!
I'm writing a Rails app and need to plug in this little bit of jQuery code, but I don't really know how to get it to work. Here's my controller code:
class ChatroomController < ApplicationController
def send_data
#role = Role.find_by_id(session[:role_id])
render :juggernaut do |page|
page.insert_html :bottom, 'chat_data', "<b>#{#role.name}:</b> #{h params[:chat_input]}<br>"
end
render :nothing => true
end
end
and view code:
<h2>Chat</h2>
<html>
<head>
<%= javascript_include_tag :defaults, :juggernaut %>
<%= juggernaut %>
</head>
<body>
<div id='chat_data', class="chatbox">
</div>
<br>
<%= form_remote_tag(
:url => { :action => :send_data },
:complete => "$('chat_input').value = ''" ) %>
<%= text_area_tag( 'chat_input', '', { :rows => 3, :cols => 70, :id => 'chat_input'} ) %>
<%= submit_tag "Send" %>
</form>
</body>
</html>
Now, I need to make the chatroom always scroll down to the bottom when any user sends a new message. But also, when the current user has manually scrolled up, disable this sort of behaviour. I found some jQuery code here: Scrolling Overflowed DIVs with JavaScript
Now I don't know to get it to work. I pasted into application.js:
$("#chat_data").each( function()
{
var scrollHeight = Math.max(this.scrollHeight, this.clientHeight);
this.scrollTop = scrollHeight - this.clientHeight;
});
I've also added <%= javascript_include_tag 'jquery', 'application' %> to the head of my view.
But when my chatroom log fills up, the scrollbar appears but does not automatically move to the bottom as new messages come through.
The problem seems to be that the code you've inserted is only run once, at the beginning of the script.
I don't know a lot about jquery so this is just a general solution.
function sub(data) {
$('#chat_data').each( function () {
var s_top = this.scrollHeight - this.clientHeight;
var scl = this.scrollTop == s_top;
this.innerHTML += '<br/>' + data;
if ( scl ) this.scrollTop = s_top + this.clientHeight;
})
};
problem is that now when you receive new data from the server you must add it into the #chat_data by calling sub("text that goes into chat window").
you'll have to replace
page.insert_html ....
with something that sends the rjs to the client, like:
page.call( :sub, data)
hope it helps.