Preventing a page from getting opened - ruby-on-rails

When user clicks on a chart, I get some ID in the Javascript side of its onclick event and pass that as a query param to the page that I want to open and I say
window.open(go_to);
which goto will have that query param in it for exampel like "http://localhost:3000/myapp/people?provider=134"
so now it hits the index action method in the Rails side, in there I also get another variable for logged in user:
def index
cur_user_id = current_user.id
provider_id = params[:provider]
# call the REST service and pass those two params
# and get the appropriate JSON back from the service
if provider_id == cur_user_id
render nothing: true
end
end
The problem I have is with this logic:
if provider_id == cur_user_id
render nothing: true
end
So if logged in user is the same as the provider I don't want to open that page or show anything. Render Nothing is helping with not showing anything but still the window.open part of the code from Javascript is opening the page, blank tho
How can I tell it hey don't even open the new page?

You can use ruby code mixed with javascript:
var provider_id = $('#provider_id').val();
var go_to = '/path/to/somewhere?provider_id=' + provider_id;
if(provider_id == <%= current_user.id %>) {
alert('You cannot go there!');
} else {
window.open(go_to);
}
Remember that the ruby code will be executed first (on server-side), then your HTML/javascript will be generated, and finally be executed on the client-side. So <%= current_user.id %> inside the javascript will just print the value in there, like if it was hard-coded in JS.
It seems that you didn't know about the ruby code inside the javascript parts, to give you an example, try this in one of your view using Javascript:
# this is on the server-side, it will be generated:
<script type="text/javascript">
<%= "alert('hello Eric!');".html_safe %>
</script>
# on the client-side, you will receive this generated HTML content:
<script type="text/javascript">
alert('hello Eric!');
</script>
For HAML:
:javascript
#{"alert('hello Eric')".html_safe}
var hello = '#{"hello World!"}';
alert(hello);

Related

AJAX not rendering as it should

I am at a loss with what’s stopping my code not rendering AJAX where it should be, I have a less serious JS ‘Parse error’ which I can’t work out also.
I have a default prevented rails form_for where upon submit event jQuery finds the element and its attribute values, posts them to the model via appropriate action, model then responds with the new object and is supposed to render the JSON via a jbuilder form.
All is fine when I get the page to render via a redirect, but not by a render ‘create’, content_type: :json, error displayed is a missing template error. I also see from network response that it ‘failed to load response data’. views/reviews/create.json.jbuilder is saved is in the correct place I believe, class and id names are all correct I believe, files and folders are named correctly and in the right place I believe, I can’t see anything wrong? Unsure whether it’s a jbuilder error, a controller syntax error, or a jQuery syntax error. Here is my code:
controllers/reviews_controller.rb:
def create
#restaurant = Restaurant.find(params[:restaurant_id])
#review = #restaurant.reviews.new(params[:review].permit(:thoughts, :rating))
if #restaurant.reviews.find_by user_id: current_user.id
flash[:notice] = "You already reviewed this restaurant!"
redirect_to restaurants_path
else
#review.user = current_user
#review.save
# redirect_to restaurants_path, will do a redirect, but defeats AJAX purpose!
render 'create', content_type: :json # results in a missing template error #'missing templete reviews/create' error
end
end
Assets/restaurants/restaurants.js:
$(document).ready(function(){
$('.new_review').on('submit', function(event){
event.preventDefault();
var reviewList = $(this).siblings('ul');
var currentRestaurant = $(this).parent();
$.post($(this).attr('action'), $(this).serialize(), function(review){
if review # This line results in Uncaught SyntaxError: Unexpected Identifier
var newReview = Mustache.render($('#review_template').html(), review);
reviewList.append(newReview);
currentRestaurant.find('.review_count').text(review.restaurant.review_count)
currentRestaurant.find('.average_rating_number').text(review.restaurant.average_rating);
currentRestaurant.find('.average_rating_stars').text(review.restaurant.average_rating_stars);
}, 'json');
});
});
views/restaurants/index.html.erb (jbuilder template element):
<template id='review_template'>
<li>
<strong>{{ star_rating }}</strong> -*- {{ thoughts }}
</li>
</template>
views/reviews/create.json.jbuilder:
json.thoughts #review.thoughts
json.star_rating star_rating(#review.rating)
json.restaurant do
json.average_rating number_with_precision(#restaurant.average_rating,
precision: 1)
json.average_rating_stars star_rating(#restaurant.average_rating)
json.review_count pluralize(#restaurant.reviews.count, 'reviews')
end
Been on this for hours now trying to solve this one, pfff!! any idea where I’m going wrong folks? Am I doing something really dim somewhere here? Thank you.
As you can see in the error message, rails is looking for a file called reviews/create[extension] or application/create[extension] and extensions allowed are .erb, .builder, .raw, .ruby, .coffee or .jbuilder.
I suggest to change you ajax call to ask for js, not json, create a file called reviews/create.js.erb, and add this kind of code :
<% if #restaurant.reviews.find_by user_id: current_user.id %>
// do the code to show the error message in javascript
<% else %>
reviewList.append("<%= j(render('create')) %>");
currentRestaurant.find('.review_count').text(<%= pluralize(#restaurant.reviews.count, 'reviews') %>)
currentRestaurant.find('.average_rating_number').text(<%= number_with_precision(#restaurant.average_rating, precision: 1) %>);
currentRestaurant.find('.average_rating_stars').text(<%=star_rating(#restaurant.average_rating) %>);
<% end %>
This code should be executed after the success of the creation. You also have to create a file called reviews/_create.html.erb with the html you want to show. Finally, you have to delete some logic in the javascript and in the controller.

Jquery not firing events after first action call

I implemented a feature where a user can update an objects attribute to true/false directly from the table output on click. Everything is working as it should for the first time, but after the ajax call is being done I cannot do it anymore. Maybe the DOM failed to load?
I have my table:
<div id="dashboard_pages_table" class="small-12 columns">
<%= render partial: 'layouts/admin/dashboard_pages_table', locals: {parents: #parents} %>
</div>
And the coffeescript which is listening to a click event on one td element:
jQuery ->
$('#sortable .onoff').on "click", (event) ->
page_id = event.target.parentElement.parentElement.id.match(/\d+$/)[0]
$.post 'update_row_onoff',
page_id: page_id
The coffeescript is calling the update_row_onoff method in my controller:
def update_row_onoff
#target_page = Page.find(params[:page_id])
if #target_page.active
#target_page.update_attribute(:active, false)
else
#target_page.update_attribute(:active, true)
end
respond_to do |format|
format.js
end
end
And then the update_row_onoff js.erb file is reloading the contents of the dashboard_table:
$('#dashboard_pages_table').html("<%= escape_javascript(render partial: 'layouts/admin/dashboard_pages_table', locals: {parents: #parents}) %>");
Why is it not working after the first successful post action?
Use this instead
$("body").on "click", "#sortable .onoff", (event) ->
Event handlers are bound only to the currently selected elements; they must exist on the page at the time your code makes the call to .on(). To ensure the elements are present and can be selected, perform event binding inside a document ready handler for elements that are in the HTML markup on the page. If new HTML is being injected into the page, select the elements and attach event handlers after the new HTML is placed into the page. Or, use delegated events to attach an event handler, as described next.
For details go to the api documentation, http://api.jquery.com/on/

Can I display a slow-loading web page incrementally?

I'm working on mashup that scrapes a couple of sites for data. I want to scrape and cache the data on demand rather than index the entire sites.
The first time the data is fetched, the operation can be extremely slow--at least a couple of minutes.
What's the best practice for displaying pages line-by-line like this? Is there a way to display the page dynamically, showing data as it's fetched?
Related:
How to display HTML to the browser incrementally over a long period of time?
How to create an Incremental loading webpage
How to make sure an HTML table renders incrementally
Display the result on the webpage as soon as the data is available at server
I've used jquery to allow each expensive partial to be rendered on clicking a button:
view:
#book_forecast
= link_to 'See Sales Estimates' , book_forecast_work_update_path(:work => #work), :remote => true
book_forecast.js.erb:
$( "#book_forecast" ).html( "<%= escape_javascript( render( :partial => 'works/work_update/evaluation_forecast', :locals => { :work => #work} ) ) %>" );
work_update controller:
def book_forecast
#work = Work.find(params[:work])
respond_to do | format |
format.js
end
end
works/work_update/evaluation_forecast.html.haml:
# slow-loading code
The downside is that the user has to click on a button to render each partial, but on the other hand, using jquery instead of rendering as normal means the expensive code doesn't run when the page loads.
You can also use a 'loading' icon so that the user's got something to look at whilst the heavy code runs, something like:
same view:
#loading
%h2
.notice
Loading, please wait...
js:
$(document).ready(function () {
$('#loading')
.hide()
.ajaxStart(function() {
$(this).show();
})
.ajaxStop(function() {
$(this).hide();
})
;
It looks like Rails Live Streaming is one approach. Disadvantage is that it appears to be highly web server dependent and touchy. Here's a tutorial:
http://tenderlovemaking.com/2012/07/30/is-it-live.html
class MyController < ActionController::Base
include ActionController::Live
def index
100.times {
response.stream.write "hello world\n"
}
response.stream.close
end
end

Pass js variable to server code

I have a form with 2 inputs and button. An user put feed url in the first input and press the button:
<%= link_to "get name", { :controller => 'Feeds', :action => "get_title" },
:remote => true, :class=>'btn btn-mini' %>
Here is controller method
def get_title
respond_to do | format |
format.js {render :layout => false}
end
end
And here is a get_title.js.erb:
var url = $( "#feed_url" ).val();
console.log(url);
$( "#feed_name" ).val("<%= #proxy.title(url) %>");
I get value of the first input and want to pass it as parameter to Ruby class. But in this case I get the error:
ActionView::Template::Error (undefined local variable or method `url' for #<#<Class:0x41ec170>:0x41ef968>):
1: var url = $( "#feed_url" ).val();
2: console.log(url);
3: $( "#feed_name" ).val("<%= #proxy.title(url) %>");
Rails this that 'url' is a Ruby variable, but not JS one.
How can I pass JS variable to Ruby code ?
Thanks in advance.
Remember that any ERB (ruby) code is executed server side, while Javascript is, of course, rendered client side. As a result, your line <%= #proxy.title(url) %> is rendered WAY before that url value is ever evaluated. The solution to your situation is more along the lines of passing data to Rails, and rendering the response. Three things to facilitate this (bearing in mind that this is only one approach, and I'm sure there are plenty of others, and possibly better ways of doing this):
1- Your link_to won't post the user-input URL value because it is not POSTing the form. Instead, change the surrounding form to use :remote=true, and use a typical form.submit button rather than this link. Your form with the URL value will be submitted (and it will be asynchronous).
2- In your controller, render your title like you were trying to do, doing something along these lines:
def get_title
render :text=>#proxy.title(params[:url])
end
3- Bind to the ajax:success event, something along these lines:
$("form#myForm").bind("ajax:success", function(event, data, status, xhr){
$( "#feed_name" ).val(data) // data, in this case, is the rendered `#proxy.title(url)` we did in step 2.
})
Hope that makes sense. Let me know if it does not.

rails navigation and partial refresh

Thanks for your time!
I get some reports data on my hand and I want to present these data on the web. The view of the HTML will be divided into two parts, the left part and the right part. There's a tree view in the left part consisting of the report names. In the right part presents the contents of the report.
What I want to achieve is when I click the report name on the left part, it will call an Action in the Controller, and passed the report name as parameter. The Action will fetch the data from the database and represent the data in the right part. And now I am stuck on how to realize this kind of view.
I've Googled a lot on the Internet and found Frameset, Partials or Ajax may capable of this. Because I've never developed web applications before and also new to Rails. So can anyone give me some advise or suggestion?
Things I've already known :
I've used Frameset to accomplish a view like this. But I found it needs a lot of .html files and all these .html files are static. And many people don't suggest it at all.
Then I've Googled Partials. But it seems Partials don't call the Action. It directly loads the _partials.html.erb to the main view. And besides, how can I control the layout? Using CSS?
I've never used Ajax.
If you want a fluid, seamless transition between one report and another, you should use both AJAX and Partials.
The way that it works is something like:
Make a left column in the html that has some links
Make the right column inside a partial
Assign the links to jQuery listeners to call the AJAX.
I'll put a bit of code here to show how it works:
Controller:
def index
reports = Report.all
if params[:report_id]
reports = Report.find(params[:report_id]
end
respond_to do |format|
format.html
format.js { render :template => "update_reports" }
end
end
update_reports.js.erb (in the same folder as the report views):
$('#report_viewer').html('<%= escape_javascript render :partial => "report_detail" %>');
In your view:
<div style=float:left>
<ul>
<li><%= link_to "Some report", "#", :class => "ajax" %></li>
</ul>
</div>
<div style=float:right id="report_viewer">
<%= render :partial => "report_detail" %>
</div>
<script type='text/javascript'>
$(document).ready(function() {
$(".ajax").click(function(e) {
$(this).ajax("your route to the action");
}
});
</script>
I think it's basically this, now let me explain a few things:
I don't remember if you have to do this, but in my case I created a new custom route to force the call to the action to be a json call instead of a html one. You can do this by adding :format => "js" to your route
You must name all your partials like "_yourname.html.erb". Rails won't recognize partials without the leading underscore.
In the controller, everything that comes after "format.js" is optional, you don't need to specify the template name, and if you don't Rails will look for the file index.js.erb.
The update_reports.js.erb file is basically a callback javascript that executes to update the current page. It finds the div where the partial is, and updates it rendering a new partial with the new report.
In the view, the link to change the report don't need to be a link at all if you're using the jQuery.click listener, but if it is a link, it must have the href as "#", or else the browser will just try to redirect to that location.
There are several ways to hook your link to the ajax function, I just chose the one I like it better, but you also could have a named function and call it in the html tag "onClick='yourFunction()'".
You need jQuery to call ajax like this. If you're sing Rails 3.0 or lower, you should replace the default Prototype with jQuery, because it's much better (IMHO), but I think prototype also have some ajax features.
It may seem complicated, but once you get the idea of it it'll become simple as writing any other action.
In the js callback file you could also add an animation to smooth the transition, like a fading. Look for the jQuery fade function for more info on this.
This is quite an open question so don't take this answer verbatim, but merely as a guide.
app/views/layouts/reports.html.erb
<!DOCTYPE html>
<html itemscope itemtype="http://schema.org/">
<head>
# omitted
</head>
<body>
<div id="body-container">
<div id="left-column">
<ul id="reports-list">
<% for report in #reports %>
<li><%= link_to report.name, report %></li>
<% end %>
</ul>
</div>
<div id="right-column">
<%= yield %>
</div>
</div>
</body>
</html>
app/controllers/reports_controller.rb
class ReportsController < ApplicationController
before_filter { #reports = Report.all }
def index
end
def show
#report = Report.find(params[:id])
end
def edit
#report = Report.find(params[:id])
end
def new
#report = Report.new
end
def update
# omitted
end
def create
# omitted
end
def destroy
#report = Report.find(params[:id])
end
end
routes.rb
YourApp::Application.routes.draw do
resources :reports
root to: "reports#index"
end
This would achieve the effect your after using just rails, of course adding ajax could add a better user experience.

Resources