I am implemeting a facebook application in rails using facebooker plugin, therefore it is very important to use this architecture if i want to update multiple DOM in my page.
if my code works in a regular rails application it would work in my facebook application.
i am trying to use ajax to let the user know that the comment was sent, and update the comments bloc.
migration:
class CreateComments < ActiveRecord::Migration
def self.up
create_table :comments do |t|
t.string :body
t.timestamps
end
end
def self.down
drop_table :comments
end
end
controller:
class CommentsController < ApplicationController
def index
#comments=Comment.all
end
def create
#comment=Comment.create(params[:comment])
if request.xhr?
#comments=Comment.all
render :json=>{:ids_to_update=>[:all_comments,:form_message],
:all_comments=>render_to_string(:partial=>"comments" ),
:form_message=>"Your comment has been added." }
else
redirect_to comments_url
end
end
end
view:
<script>
function update_count(str,message_id) {
len=str.length;
if (len < 200) {
$(message_id).innerHTML="<span style='color: green'>"+
(200-len)+" remaining</span>";
} else {
$(message_id).innerHTML="<span style='color: red'>"+
"Comment too long. Only 200 characters allowed.</span>";
}
}
function update_multiple(json) {
for( var i=0; i<json["ids_to_update"].length; i++ ) {
id=json["ids_to_update"][i];
$(id).innerHTML=json[id];
}
}
</script>
<div id="all_comments" >
<%= render :partial=>"comments/comments" %>
</div>
Talk some trash: <br />
<% remote_form_for Comment.new,
:url=>comments_url,
:success=>"update_multiple(request)" do |f|%>
<%= f.text_area :body,
:onchange=>"update_count(this.getValue(),'remaining');" ,
:onkeyup=>"update_count(this.getValue(),'remaining');"
%> <br />
<%= f.submit 'Post'%>
<% end %>
<p id="remaining" > </p>
<p id="form_message" > </p>
<br><br>
<br>
if i try to do alert(json) in the first line of the update_multiple function , i got an [object Object].
if i try to do alert(json["ids_to_update"][0]) in the first line of the update_multiple function , there is no dialog box displayed.
however the comment got saved but nothing is updated.
it seems like the object sent by rails is nil or cant be parsed by JSON.parse(json).
questions:
1.how can javascript and rails know that i am dealing with json objects?deos ROR sent it a object format or a text format?how can it check that the json object has been sent
2.how can i see what is the returned json?do i have to parse it?how?
2.how can i debug this problem?
3.how can i get it to work?
Related
In my Rails 7 (with bootstrap) app I need to create a table with all users transaction that has on each row an arrow that when clicked on, expands that row inside which is a new table (that's what I think it is - a new table). I think the attached design will better explain what I mean:
To achieved that I could try javascript with something like this https://jsfiddle.net/Wfxpu/180/ but I'm not sure if it's a modern approach. I'm wondering is it possible to a write something like this without any JS code using turbo maybe? or even pure HTML ?
Here is what I was trying to do:
# transaction controller
class TransactionsController < ApplicationController
def index
response = client.transactions.list(platform_id: current_user.platform_id, page: 1, per_page: 100)
#transactions = response.body['data']
end
private
def client
#client ||= TestAPI::Client.new
end
end
so then sample table would be:
# views/transactions/index
<table>
<% #transactions.each do |transaction| %>
<tr><%= transaction.amount %></tr>
<% end %>
</table>
You could try using a remote link_to as your "arrow" to invoke show_more action.
<%= link_to your_path, remote: true do %>
your image
<% end %>
In your controller you would need to respond to that with
def show_more
...
#data_to_show = some_database_call
respond_to do |format|
format.js
...
end
end
then in show_more.js.erb you can run javascript using the data that you just received.
var content = document.querySelector("#hidden-section-1");
content.innerHTML = ""
content.insertAdjacentHTML("beforeend",
"<%= j render('layouts/your_template', data: #data_to_show) %>");
where layouts/your_template would be a template with all the stuff you want to show(the "other" table you mentioned)
<h1> merchant: <%= data.merchant %> <h1>
...
<p> transaction_id: <%= data.transaction_id %> </p>
...
I created a file inside my javascript folder "load-contacts.js" in hopes of trying to load the collections as well as the pagination via jquery.
$(document).on('turbolinks:load', function() {
$('#contacts').html("<%= j render(#contacts) %>");
$('#paginator').html("<%= j paginate #contacts, remote: true %>");
});
When I run this code it actually works and change the part where it intended to change. However, it is only rendering it as text as it instead of the collection and the pagination (kaminari gem).
Here's how it looks like:
Any idea what am i missing here?
UPDATES:
Here's my contacts controller
class ContactsController < ApplicationController
before_action :find_params, only: [:edit, :update, :destroy]
def index
# catch the group id from params in session
session[:selected_group_id] = params[:group_id]
#contacts = Contact.by_group(params[:group_id]).search(params[:term]).order(created_at: :desc).page params[:page]
end
def autocomplete
#contacts = Contact.search(params[:term]).order(created_at: :desc).page(params[:page])
render json: #contacts.map { |contact| { id: contact.id, value: contact.name } }
end
def new
#contact = Contact.new
end
def create
#contact = Contact.new(contact_params)
if #contact.save
flash[:success] = "Contact was successfully created."
redirect_to(previous_query_string)
else
render 'new'
end
end
def edit
end
def update
if #contact.update(contact_params)
flash[:success] = "Contact successfully updated."
redirect_to(previous_query_string)
else
render 'edit'
end
end
def destroy
#contact.destroy
flash[:success] = "Contact successfuly deleted."
redirect_to contacts_path
end
private
def contact_params
params.require(:contact).permit(:name, :email, :phone, :mobile, :company, :address, :city, :state, :country, :zip, :group_id, :avatar)
end
def find_params
#contact = Contact.find(params[:id])
end
def previous_query_string
session[:selected_group_id] ? { group_id: session[:selected_group_id] } : {}
end
end
Here's the part for the kaminari gem pagination:
<div class="card-footer">
<div class="pagination justify-content-center" id="paginator">
<%= paginate #contacts, remote: true %>
</div>
</div>
And here's where I am suppose to be rendering the contacts inside the views/contacts/index.html.erb:
<table class="table" id="contacts">
<%= render partial: "contact", object: #contacts, remote: true %>
</table>
you can't use j render outside .erb. Because j render is method of rails, your js just know it is string
what you need are use script tag inside your html.erb, or use rails ajax. It is working as expected
reference: How to render partial on the same page after clicking on link_to with AJAX
have you tried escape_javascript before calling render.
something like:
<%= escape_javascript render(my_partial) %>
Hey guys I need to know the answers to some questions. First of all how do I handle ajax call when I am not returning anything from the controller because it always ask me for the view for the called function but since I have called that function using ajax how can it have a view.(I am using it in ROR(ruby on rails))
bookmark.html.erb
<div class="row">
<div id="page-wrapper-lbms" class="small-12 medium-12 large-12 columns dashboard-page">
<input type="hidden" id="bookmarked_against_req_id" value=<%= #data2%> />
<%
if #requests != nil
#requests.each.with_index do |request,index|
if request != nil
%>
<header>
<div style="text-align:center">
</span>
</div>
<h2 style="text-align:center"><%= request.title %></h2>
<div style="text-align:center">
<h6 style="color:#aaaaaa"><strong><%= request.is_service%></strong></h6>
</div>
</header>
<div class ="row">
<div class="small-12 medium-12 large-8 columns">
<div id="request-content-lbms" class="panel">
<p>
<%= request.description%>
</p>
</div>
<div id="list-request-cat-lbms">
<span class="fa fa-tag"></span>
<% if #tag[index] != nil
#tag[index].each do |cat| %>
<%= cat.title %>
<%end%>
<%end%>
</div>
</div>
<aside class="small-12 medium-12 large-4 columns" style="padding-left:10%">
<h3 ><strong>Syed Muhammad Ali Gardezi</strong></h3>
<time><%= request.created_at %></time><br />
<%end%>
<button class="button tiny">Schedule Meeting</button>
</aside>
</div>
</div>
<%end%>
<%end%>
<script>
function ajax_bookmark(abc , cde){
var cde = $('#bookmarked_against_req_id').val();
$.ajax({
type: "GET",
url: 'bookmark_request',
data: {
d:abc,
d1:cde
},
error:function(){
},
dataType: 'json',
//beforeSend:function(){},
//complete:function(o,s){},
success:function(data){
location.href ="/requests/bookmark"
},
type: 'get'
});
}
</script
bookmark_request
def bookmark_request()
data = params[:d]
data1 = params[:d1]
data2 = data1#["$oid"]#.split('"' , 6)
#asd
request_bookmarked = Request.getRequest(data)
bookmarked_against_Request = Request.getRequest(data2)
request_bookmarked_2 = request_bookmarked
bookmarked_against_Request_2 = bookmarked_against_Request
#asd
if bookmarked_against_Request_2[:favourites]
bookmarked_against_Request_2[:favourites] << request_bookmarked[:id]
else
bookmarked_against_Request_2[:favourites] = Array.new
bookmarked_against_Request[:favourites] = Array.new
bookmarked_against_Request_2[:favourites] << request_bookmarked[:id]
end
if request_bookmarked_2[:favourites_of]
request_bookmarked_2[:favourites_of] << bookmarked_against_Request[:id]
else
request_bookmarked_2[:favourites_of] = Array.new
request_bookmarked[:favourites_of] = Array.new
request_bookmarked_2[:favourites_of] << bookmarked_against_Request[:id]
end
request_bookmarked.update(Hash['favourites' ,request_bookmarked_2[:favourites]])
bookmarked_against_Request.update(Hash['favourites' ,bookmarked_against_Request_2[:favourites]])
#asd
bookmarks = Bookmark.where(request_id: bookmarked_against_Request[:id]).first()
#asd
b = bookmarks
if bookmarks != nil
#bookmarks.each { |bookmark| bookmark.update_attributes(corsponding_requests: request_bookmarked[:id]) }
b[:corsponding_requests] << request_bookmarked[:id]
b.update(Hash['corsponding_requests' , b[:corsponding_requests]] )
else
bookmark = Hash.new
bookmark["owner_req"] = session[:user]
bookmark["request_id"] = bookmarked_against_Request[:id]
bookmark["corsponding_requests"] = Array.new
bookmark["corsponding_requests"] << request_bookmarked[:_id]
Bookmark.createBookmark(bookmark)
end
flash[:notice] = "Request has been bookmarked successfully."
end
Now when ever I run this function i need it to go into the success function of ajax in bookmark.html.erb but the problem is it says I can't find the view. What is it that I am doinf wrong over here
Second when ever I need to return something I have read that I need to make a html helper but I can't figure out what a html helper is. and how can I return a string back to ajax function
Any help will be appreciated (I am new to AJAX)
You'll probably want to take a look at the rails guides - they're a great source of general grounding: http://guides.rubyonrails.org. For returning different responses based on the request format (ajax vs. html), see http://guides.rubyonrails.org/action_controller_overview.html#rendering-xml-and-json-data and #davidwessman's answer above.
Generally, you'll typically return JSON data from an ajax request; so, for instance, in the controller you might say
respond_to do |format|
format.js {render json: #my_ojects.to_json}
end
Note that there are several ways to render json, including the gems like rabl. If you're returning json like that, you don't need to render a rails view (unless you're using a view to build your json, like rabl does or is hinted at in davidwessman's answer with js.erb).
Hope that helps.
Gonna try to answer you :
First:
If you have a special action for your ajax call you only need:
def custom_action
end
This will render the custom_action.js.erb if called with ajax, for example a form with remote: true.
If you call one of your CRUD-actions and need to be able to respond to multiple formats:
def show
#book = Book.find(params[:id])
respond_to do |format|
format.html # will render show.html.erb
format.js # will render show.js.erb
end
end
If you put a link in your view:
link_to('Remote Ajax Call', book_path(#book), remote: true)
This link will call the method show and render as :js.
It will look for app/views/books/show.js.erb which may look something like:
<% if #book.present? %>
$('#book-title').html("<%=#book.title%>");
<%end%>
And in your view, close to the link above, this will then change the text inside a html-tag with I'd book-title.
<div id="book-title">
</div>
Second:
I do not understand what you mean by
...need to return something...
Can you add an example?
Hope that this can help you a bit.
Edit:
This is my try to understand what you want to do:
In your view:
<%= hidden_field_tag :bookmarked_against_req_id, value: #data2 %>
<% if #requests != nil %>
<% #requests.each.with_index do |request,index| %>
<% if request.present? %>
<%= link_to 'Bookmark it', bookmark_request_path(request), remote: true %>
<%= request.title %>
<%= request.is_service%>
<%= request.description%>
<% if #tag[index].present? %>
<% #tag[index].each do |cat| %>
<%= cat.title %>
<%end%>
<%end%>
<%= request.created_at %>
<%end%>
<%end%>
<%end%>
In your controller:
def bookmark_request()
request_bookmarked = Request.getRequest(params[:id]) # the params[:id] should be set in the request.
bookmarked_agains_Request = Request.getRequest(params[:bookmarked_against_req_id) # This is defined in your hidden_field_tag
When your action bookmark_requestis done. If the request was AJAX you will render the JavaScript-file bookmark_request.js.erb
Here you should have the script you put in your view:
If the resource is saved, you should put JavaScript to render 'Success' or if it failed you should do otherwise.
The flash[:notice] = "Request has been bookmarked successfully."will not be understood by your AJAX-request.
I am using Rails 4.1.1 Why do I have to click twice for jQuery effect to show?
I have been Googling but I cannot find any solutions, not sure if there's something wrong with my jQuery code or....
When I click "Submit" for the first time, the effect didn't appear but the POST action is already called, when I click for the second time, the effect appeared and the POST action is called.
Please check below my create.js.erb, users_controller.rb, new.html.erb
create.js.erb
function isValidEmailAddress(emailAddress) {
var pattern = new RegExp(/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))#((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i);
return pattern.test(emailAddress);
};
$("form").submit(function( event ) {
var email = $("input#user_email").val();
if (email == "") {
$("h1").text( "Email cannot be empty" ).show().fadeOut( 3000 );
return;
} else if (!isValidEmailAddress(email)) {
$("h1").text( "Email address is not valid" ).show().fadeOut( 3000 );
}else {
$("h1").text( "Correct email address" ).show();
}
event.preventDefault();
});
new.html.erb
<%= form_for #user, remote: true do |f| %>
<p>
<%= f.text_field :email %>
</p>
<p>
<%= f.submit :Submit %>
</p>
<% end %>
<h1></h1>
<% #users.each do |user| %>
<tr>
<li><%= user.email %></li>
</tr>
<% end %>
users_controller.rb
class UsersController < ApplicationController
def new
#users = User.all
#user = User.new
end
def create
#user = User.new(user_params)
respond_to do |format|
format.html { redirect_to '/' }
format.js
end
#user.save
end
private
def user_params
params.require(:user).permit(:email)
end
end
Turbo links might be causing this issue -try disabling turbo links in your app , that has worked for me In the past when I've had a similar problem
You're getting the process wrong. When you submit a remote form in Rails, the app sends an AJAX request to the create action and then evals the received js code (from your create.js.erb). So first time you click the button, nothing happens.
The js view is not the place to handle validations, you should put that somewhere in your assets/javascripts. The js view is a place for updating the html view of the page with the form (e.g. adding records to the list, showing received errors, reseting the form).
I have a long block of comments on a view of model Page. Instead of showing all the comments on page load, I'm trying to create a "view more" button that shows the next ten comments. The button sends an ajax request to the controller, which then renders this block using jquery:
_view_more.html.erb
<% comments.each_with_index do |comment, index|%>
<% if (( index > #start_number) && (index < #end_number) ) %>
<%= comment.text %>
<% end %>
Let's say I always want to show the next 10 comments. I would just set #start_number = #start_number + 10 and #end_number = #end_number + 10
in the controller, but instance variables get reset, so #start_number would be nil. How can I set a variable that increases by 10 upon every ajax request?
"view more" button
<%= link_to "view more", view_more_page_path, remote: true %>
pages_controller.rb
def view_more
#page = Page.find(params[:id])
respond_to do |format|
format.html { redirect_to root_path }
format.js
end
end
view_more
$("#comments-body").append("<%= escape_javascript(render 'view_more') %>");
I will use haml and coffee-script
When rendering comments you put an html5 data attribute with the id of the comment:
#view where to render comments
%div#comments-wrapper{:"data-pageid" => #page.id}
=render #comments
=link_to "View more", "#", id: "view-more-link"
The comment partial
#comments/_comment.html.haml
%p.single-comment{:"data-commentid" => comment.id}
=comment.body
application.coffee
$ ->
$("#view-more-link").on 'click', ->
last_comment_id = $("#comments-wrapper .single-comment").last().data('commentid')
page_id = $("#comments-wrapper").data("pageid")
$.ajax
url: "/comments/view_more"
dataType: "script"
data:
last_comment_id: last_comment_id
page_id: page_id
comments_controller
def view_more
#page = Page.find(params[:pageid])
if params[:last_comment_id]
#comments = #page.comments.where("comments.id > ?", params[:last_comment_id]).limit(10)
end
respond_to do |format|
format.js
end
end
comments/view_more.js.erb
$("#comments-wrapper").append("<%= escape_javascript(render #comments) %>");
Note: I don't how your routes were set up so I put the page.id as a data-attribute as well
I would use already implemented pagination gems kaminari or will_paginate. I'll create this example using will_paginate.
First of all, it's important to say that your approach is incorrect, because it loads all comments every view_more request. If you want to show 10 comments, makes sense select only they from database, right? The pagination gem will do it for you!
Let's to the code:
"view more" button
<%= link_to "view more", view_more_page_path, remote: true, id: 'view-more-btn' %>
pages_controller.rb
def view_more
#comments = Page.find(params[:id]).comments.paginate(page: params[:page], per_page: 10)
respond_to do |format|
format.html { redirect_to root_path }
format.js
end
end
_view_more.html.erb
<% #comments.each do |comment| %>
<%= comment.text %>
<% end %>
view_more.js.erb
$("#comments-wrapper").append("<%= escape_javascript(render 'view_more') %>");
# We need to update the 'view more' link with the next page number
$('#view-more-btn').attr('href', '<%= view_more_page_path((params[:page] || 0) + 1) %>')
is it not good to update an hidden variable before making ajax call with the count..?
var currentVal = parseInt($("[type=hidden]").val());
$("[type=hidden]").val( currentVal + 1 );
add the hidden field right at the begining of the comments section with default value as "0"
<input type="hidden" name="hiddenId" id="hiddenId" value="0">
Hope it will help
If you want a quick and dirty approach, you could save start_number and end_number inside a cookie.
But keeping track of what needs to be rendered next on client side would be a right thing to do.