So the problem I am having is getting my like/unlike button to refresh with ajax in my Ruby on Rails app, here is my code:
app/views/_comment.html.haml
- likes = comment.likes
%div.comment{id: "comment-#{comment.id}"}
.comment-avatar
.medium-user-avatar.avatar-canvas
- if comment.user.avatar_url
= image_tag comment.user.avatar_url(:medium)
- else
%span.medium-user-initials.initials-decoration
= comment.user.avatar_initials
%span.comment-username= link_to(comment.user_name, "#")
%span.comment-body~ markdown(comment.body)
.comment-time
= time_ago_in_words(comment.created_at) + " ago"
- if can? :like, comment
= " · "
- if likes.find_by_user_id(current_user.id).nil?
= link_to "Like", like_comment_path(comment), method: :post, remote: true
- else
= link_to "Unlike", unlike_comment_path(comment), method: :post, remote: true
- if comment.user == current_user
= " · "
= link_to "Delete", comment_path(comment), method: :delete, remote: true,
:data => { :confirm => "Are you sure you want to delete this comment?" }
- if likes.count > 0
.comment-likes
- likers = likes.map { |like| link_to(like.user_name, "#") }
- if likers.length > 1
- likers = likers.slice(0, likers.length - 1).join(", ").concat(" and " + likers.slice(-1))
- else
- likers = likers[0]
= "Liked by #{likers}".html_safe
app/controllers/comments_controller.rb
class CommentsController < BaseController
load_and_authorize_resource
def destroy
destroy!{ discussion_url(resource.discussion ) }
end
def like
comment_vote = resource.like current_user
Event.comment_liked!(comment_vote)
#redirect_to discussion_url(resource.discussion )
render :partial => "like"
comment_likes
end
def unlike
resource.unlike current_user
#redirect_to discussion_url(resource.discussion)
render :partial => "unlike"
comment_likes
end
def comment_likes
render :partial => "comment_likes"
end
end
and then the .js.erb files for like, unlike and comment_likes:
app/views/_like.js.erb
$(".comment-time a#like").html("<%= escape_javascript(render('.comment-time a#like'")
app/views/_unlike.js.erb
$(".comment-time a#unlike").html("<%= escape_javascript(render('.comment-time a#unlike'")
app/views/_comment_likes.js.erb
$(".comment-likes a##").html("<%= escape_javascript(render('.comment-likes a##'")
Currently clicking like will update the database but will not show the changes until a page refresh, I just want to refresh the individual div's with ajax. A little more information about the div's could help so the ruby creates the html and contained in that as an example is or when already liked I just need to refresh these divs to show the latest from the database aswell as which contains http://localhost:3000/comments/7/unlike 500 (Internal Server Error)"
The rest of the scripting has been done in coffeescript if that matters? I read that the controller functions should use .js.erb so hope this isn't affecting it all. (Im sure my js.erb's are wrong)
I'm not sure if this is the main problem but one thing to fix would be the render statement in your .js.erb files.
The Rails render method requires as its argument the erb template or action to render. When you call:
render('.comment-time a#unlike')
Rails will try to find a template with the name ".comment-time a#unlike" somewhere in your view path which will probably raise some kind of error. Make sure you pay attention to the difference between what's happening in your javascript (in the client) in your application (on the server.)
So one way to fix this would be as follows. First check what part of the view you want to update, for simplicity's sake because you already have the _comment partial let's use that. Second figure out which part in the dom it should replace, in your case the div with the current comment's ID. We can then do the following:
$("#comment-<%= #comment.id %>").replace_html(
"<%= escape_javascript render('comments/comment', :comment => #comment) %>"
);
This will render the _comment.html.erb partial in app/views/comments, insert the result (escaped) into the javascript in your (dis-)like.js.erb and send that back to the browser to execute. The browser will then replace_html on the comment's div (indicated by the ID.)
Because we're replacing the whole comment div you can use the same method for both like and dislike. If you need to save bandwidth you could fine tune it to only re-render the like button itself, but for now this will work.
The problem is your Controller does not know how to respond to the ajax request. By default the render method will render html.erb or in your case html.haml templates.
Unless you do something like:
respond_to do |format|
format.js { comment_likes }
end
Now if an ajax requests comes in the _comments_likes.js.erb template will be rendered.
If you have more of these ajaxy type questions there are efforts being put into rewriting the ajax guides for rails.
And by the by, please consider cleaning up your view.
Related
I'm working on a legacy app (struts 1, own framework, java 6, on WebSphere 7) where I need to change the behavior of an operation.
Let's call them (1) CopyItem, (2) AlterItem, (3) MarkAsCopied.
A button called one service which needs to be replaced with three other services.
Depending on the result of the first one, invoke the second one and so on.
I want to navigate in the end where the first one would take me (so it would look like the original behavior from user point of view).
Initially I thought I wrap every parameter I need into a form, POST it, and then on the Java side, I would call action execute for each service. Technically from CopyItemAction.execute() of CopyItem I would call AlterItem and MarkAsCopied executes as well.
I feel that this is pretty far from a clean solution.
Do you have a better idea, how to do this?
In the end I did it via synchronized Ajax. Which is definitely bad practice, so I do not recommend if you have the access on the backend code. However in my case now it turned out to be a working not-that-ugly solution. Based on Dave Newton's (https://stackoverflow.com/users/438992/dave-newton) proposal.
Keep that in mind the struts actions are still called according to the configuration defined in struts-config.xml.
I created the js function below and invoked the services with the specified forms one by one. This way a form (POST) submit calls the struts action, but doesn't navigate away. After they run successfully I redirected the page.
function submitForm(form) {
if (form !== null && form !== undefined) {
let formData = new FormData(form);
let xhr = new XMLHttpRequest();
xhr.open('POST', form.getAttribute('action'), false);
var successResponse = false;
xhr.onreadystatechange = function() {
if(xhr.readyState == XMLHttpRequest.DONE) {
successResponse = xhr.status == 200;
}
}
xhr.send(formData);
return successResponse;
}
}
In conclusion: this is not a recommended solution, but if you're in such a situation like me, then it works.
I have a jqgrid component inside my page and the rows of the grid are create with values and actions, for example:
colum: name adresss job edit
row 1: jorge my adress architect [value]
I populate the grid with several rows from a database and aech row have a value in column edit to the personal page for editing actions.
this value have this structure:
<div><span class="edit" idperson="' + items[i].id + '">Edit</span></div>
in the same script I put this:
$('.edit').live('click', this.clickedit);
and the function is:
clickedit: function(){
$.ajax('person/personedit.htm', {
cache: false,
type : 'POST',
data : { idperson: $(this).attr('idperson')},
success : function(response){
//some actions.....
}
});
},
in my java code I have a spring controller that recieve this call from jquery and do something.
The first time I clic the Edit action in a row all is OK, but after a while working with the application I see that a single edit action is called several times so the clickedit function execute 2, or 3 or 4 times and my java code are hit the same numbers of times.
any idea about this issue???
UPDATE:
I start to using onCellSelect now. But I have a problem. In a single cell I can put 3 differents action in this way:
var actions = 'editedOK ? '<div><span class="edit1" idperson="' + items[i].id + '" >EDITOK1 </span><span class="edit2" idperson="' + items[i].id + '">EDITOK2</span></div>' : '<div><span class="editnook1" idperson="' + items[i].id + '">EDITNOOK1</span></div>';
if actions = true I see in the same cell "EDITOK1 EDITOK2" and if actions = false I see in this cell "EDITNOOK1"
everyone of this actions have associates functions with differents behaviour when I clic in it.
If I use onCellSelect it´s possible to determinate when I hit EDITOK1, when I hit EDITOK2 and when I hit EDITNOOK1. More important is to determinate between EDITOK1 and EDITOK2.
I suppose that you calls $('.edit').live('click', this.clickedit); multiple times. It's the reason why this.clickedit handler will be called more as one time.
I recommend you don't use any explicit binding of this.clickedit callback. Instead of that you can use beforeSelectRow or onCellSelect. See the answer and this one for the corresponding code examples.
I am using Visualr http://googlevisualr.herokuapp.com/ with Rails and having a good amount of success creating dynamic charts. However, I am wondering if it's possible to allow the user to click on the column in a 'column chart' and be linked to a page? I am happy to know the java version if you aren't familiar with visualr.
Thanks!
It now is available!
There has recently been an update on this issue. Therefore I want to update this SO Q&A.
Resources:
Google Visualr Github Pull Request #39
Google Visualr Github Issue #36
Code example
xxx_controller.rb
#table = GoogleVisualr::Interactive::ColumnChart.new(g, options_g)
#table.add_listener("select", "function(e) {
EventHandler(e, chart, data_table)
}")
And then in a JS file e.g. app/assets/javascripts/application.js:
function EventHandler(e, chart, data) {
var selection = chart.getSelection();
if (selection.length > 0) {
var row = selection[0].row;
var department = data.getValue(row, 0);
alert(department + " | " + row)
}
}
Google Charts (whether you access them directly or via a wrapper gem like Visualr) are simple images, so the straight answer is "No", at least not without doing some work of your own. In order to achieve this you would need to place your own transparent clickable links (or divs or whatever) over the image, in the right place, to correspond to the columns that google generate in the image.
I'd imagine this would be tricky and error prone - it might actually be easier for you to just generate the columns yourself in html and css, using the data you would previously have sent to google to set the height (in %) of the columns. Then, each column would be a seperate html element and could link to whatever you want.
So, more control = more work. As usual :)
As a hobby project I am exploring the ways to save a web page (HTML) as image, mostly programatically using c/c++/javascript/java. Till now I have come across the following ways:
Get the IHTMLElement of page body and use it to query for IHTMLElementRender and then use its DrawToDC method (Ref: http://www.codeproject.com/KB/IP/htmlimagecapture.aspx ). But the problem is that it did not work for all the pages (mostly pages having embedded iframes).
Another way which i can think of is to use some web browser component and when the pages is fully loaded then capture it using BitBlt (Ref: http://msdn.microsoft.com/en-us/library/dd183370%28VS.85%29.aspx ). But the problem is that the page I have requested may be longer than my screen size and it will not fit into the web browser component.
Any direction/suggestion to resolve above issues or an alternative approach is greatly appreciated.
If you use Python, there's pywebshot and webkit2png. Both of them have some dependencies, though.
Edit: Oops, Python is not in your list of preferred languages. I'll leave this answer here anyway, because you said "mostly" and not "exclusively".
Another (somewhat roundabout) option would be to run a server like Tomcat and use Java to call a command-line tool to take a screenshot. Googling for "command line screenshot windows" comes up with some reasonable-looking possibilities. Besides running a server, though, I don't know a good way to run local executables from javascript. This method would make it cross-browser, though, which is a plus (just make an ajax call to the script when you want a screenshot).
Unfortunately I don't actually know how to deploy war files. It might be more trouble to use Tomcat; I mentioned it because Java was a preferred language. It would be fairly simple to run XAMPP and use this PHP snippet, and you wouldn't really have to learn php:
<?php
exec("/path/to/exec args");
?>
EDIT
You know, I'm not sure that really answers your question. It's one way, but it's coming at it from the JavaScript end rather than the scripting end. If you want to do it via scripting, you could always use Selenium. It supports capturing screenshots of an entire page, and can be controlled via Java.
Well finally able to crack it by going through these two articles:
http://www.codeproject.com/KB/GDI-plus/WebPageSnapshot.aspx [c# code - IE]
http://www.codeproject.com/KB/graphics/IECapture.aspx [c++ & GDI - IE]
Can't share the code, but the above two articles will give you the best possible solution.
Also have a look at:
https://addons.mozilla.org/en-US/firefox/addon/3408/ [firefox + javascript]
Above things are still ok. BUT not guaranteed to work always. Check the below link:
How do I render the scrollable regions of a canvas with IViewObject::Draw?
If you are OK using javascript for it, I suggest going with phantomjs
Example from http://fcargoet.evolix.net/
var page = new WebPage(),
address = 'http://dev.sencha.com/deploy/ext-4.0.7-gpl/examples/feed-viewer/feed-viewer.html';
page.viewportSize = {
width : 800,
height : 600
};
// define the components we want to capture
var components = [{
output : 'feed-viewer-left.png',
//ExtJS has a nice component query engine
selector : 'feedpanel'
},{
output : 'feed-viewer-preview-btn.png',
selector : 'feeddetail > feedgrid > toolbar > cycle'
},{
output : 'feed-viewer-collapsed.png',
//executed before the rendering
before : function(){
var panel = Ext.ComponentQuery.query('feedpanel')[0];
panel.animCollapse = false; // cancel animation, no need to wait before capture
panel.collapse();
},
selector : 'viewport'
}];
page.open(address, function (status) {
if (status !== 'success') {
console.log('Unable to load the address!');
} else {
/*
* give some time to ExtJS to
* - render the application
* - load asynchronous data
*/
window.setTimeout(function () {
components.forEach(function(component){
//execute the before function
component.before && page.evaluate(component.before);
// get the rectangular area to capture
/*
* page.evaluate() is sandboxed
* so that 'component' is not defined.
*
* It should be possible to pass variables in phantomjs 1.5
* but for now, workaround!
*/
eval('function workaround(){ window.componentSelector = "' + component.selector + '";}')
page.evaluate(workaround);
var rect = page.evaluate(function(){
// find the component
var comp = Ext.ComponentQuery.query(window.componentSelector)[0];
// get its bounding box
var box = comp.el.getBox();
// box is {x, y, width, height}
// we want {top, left, width, height}
box.top = box.y;
box.left = box.x;
return box;
});
page.clipRect = rect;
page.render(component.output);
});
// job done, exit
phantom.exit();
}, 2000);
}
});
I am getting an error that is only reproducible in Firefox.
In IE, it always works.
I have an ASP.NET application that targets an applet. For most functionality, it works great, but when I run one function (a function that does a lot of different things), the methods on the java applet seems to be unavailable! When I call any functions on the java applet from Javascript code, I get the error : myApplet.myFunction is not a function .
After this has happened, I get the same error for everything that calls functions on the applet, also functionality that worked prior to this state.
The applet is not unloaded or crashed, which is proved by that the interactive functions in the applet still works. But if the applet calls a javascript function on the page that calls back to the applet, I get the same error!
Sometimes it works when I repeat the action several times.
This only happens on the public version of the web application. Not in my local web app that runs on development machine. The main difference is that the public one use login access.
Can anyone give me hint of what can be causing this?
To summairize whats happens: When I run a particular bit of javascript code, the API against the java applet becomes unabailable (Error: myApplet.myFunction is not a function).
Here is the function that causes this buggy state of the applet:
function ParseAndZoomToAddress (objString) {
var mapFrame
eval("var objArray = new Array(" + objString + ")")
if (objArray.length > 0) {
mapFrame = window.frames[0].frames['mapFrame']
if(!mapFrame) alert('mapFrame is null!')
else window.status = 'mapFrame is OK!'
var center_x = 0
var center_y = 0
if(objArray.length == 1) {
var rExp1 = /[G-P]/g
var rExp2 = /[Q-Z]/g
eval("var coordArray = new Array(" + objArray[0].replace(rExp1, '0x').replace(rExp2, ',0x') + ")")
if(coordArray.length == 2){
center_x = coordArray[0]
center_y = coordArray[1]
}else{
alert("Invalid ID " + objArray[0])
}
}else{
var center_x = objArray[1]
var center_y = objArray[2]
}
var objZoomScale = '2000' // document.SearchForm.Scale.value
var scale = mapFrame.m_yur - mapFrame.m_yll
if (objZoomScale != "") {
scale = ScaleToMeter(parseInt(objZoomScale),mapFrame.m_image_width)
}
var xll = center_x - (scale / 2.0)
var yll = center_y - 10
var xur = xll + scale
var yur = center_y + 10
center_x = (xll + xur) / 2.0
center_y = (yll + yur) / 2.0
center_x = Math.round(center_x * 1000) / 1000
center_y = Math.round(center_y * 1000) / 1000
mapFrame.ResetClipPolygon() //This calls applet methods
var lCenterSymbol = '5024,40,85,x,y,5'
var rExp = /x/gi
lCenterSymbol = lCenterSymbol.replace(rExp,center_x)
rExp = /y/gi
lCenterSymbol = lCenterSymbol.replace(rExp,center_y)
mapFrame.parent.m_CenterSymbolString = lCenterSymbol
mapFrame.ResetClipPolygon() //This calls applet methods
mapFrame.previewVisible()
mapFrame.mapVisible()
mapFrame.setMove(false) //This calls applet methods
mapFrame.ZoomToArea(xll, yll, xur, yur) //This calls applet methods
mapFrame.parent.m_CenterSymbolString = ""
}
}
Additionaly I can say that the applet is defined by the <applet> tag.
The applet is located inside a <div> tag that is hidden in javascript with style.display='none' and shown again with style.display='block'.
This is done during the 2 lines:
mapFrame.previewVisible()
mapFrame.mapVisible()
I don't know if this is the problem, but these function are called elsewhere as well where it doesn't cause this, but it might be combination of things.
I am happy to get hint's that will help me solve this, not an absolute solution.
I will accept the best hint as the answer.
Perhaps this is a timing issue. It could happen that the Firefox engine tries to execute the javascript code before the java applet has been properly loaded. To verify this add a delay before accessing the applet in javascript code. Even better use a try-catch block for your call and wrap it in a for-loop. If the call is successful exit the loop, otherwise add a delay and retry.
Edit: Based on your edits it seems that there isn't a timing issue. Since your javascript function is quite big, I would try to locate, where the problem first appears. Create a dummy applet method and call it inside your javascript method at various places. Find out after which statement the problem first appears. Also, if you haven't done that already, install Firebug and watch out for javascript errors.
I was suddenly able to reproduce it in my development environment, so it was easier to locate the problem. The problem was related to hiding/showing as I suspected, but I'm still not sure why it was a problem. It was probably some sort of timing issues as noted by kgiannakakis, so will credit him with the bounty reward.
The problem disappeared when commenting out these two lines:
mapFrame.previewVisible(); // appletDiv.style.display="none"
mapFrame.mapVisible(); // appletDiv.style.display="block"
The comment at end is basically what is done inside those functions with the div that contains the applet.
So far, so good.
The intention with the Visible functions are originally switching 2 tabs where one of them displays the applet (mapVisible), and the other displays a static image (previewVisible). If I click the preview tab, that basically runs previewVisible(), the appletDiv is hidden. If I then run my big nasty function (ParseAndZoomToAddress) then I get the error again, but this time it is somewhat expected, since the applet is hidden (and obviously FireFox does not like to work with hidden applets..).
Then I remember why I included these calls inside the ParseAndZoomToAddress function: I have to make sure the applet is visible before accessing it! So I try to re-introduce the call to mapFrame.mapVisible(), and guess what - it works! Even if the applet was hidden, it is available after the call to mapFrame.mapVisible().
Anyone have a conclusion to this?
My theory is that it causes problems to unload and reload the applet so close together in the same javascript function.
I came here through a search, as I had exactly the same problem in Firefox (not MSIE), on web pages that I knew I had not changed for years and that I knew had worked fine before in Firefox.
myApplet.myFunction is not a function
As it turns out, in my case the culprit is just having the Google Translate Tools Javascript code described at
http://translate.google.com/translate_tools?hl=en&layout=1&eotf=1&sl=bg&tl=en
After I had removed that snippet from my included Javascript file (included at the bottom of my applet page), myApplet.myFunction worked fine again!
It also fixed the mysterious problem that my applet would not always load when loading the web page. Maybe Google Translate Tools under the hood also fiddles with style.display or similar, but I did not further dig into that.
I suspect that many others who use Google Translate Tools on a Java applet page with Javascript controls are similarly affected.
This post seems to have similar problem. They claim they fixed some HTML syntactic error and then it worked in Firefox.
I had a similar problem.
We fixed it by putting the <applet></applet> inside a <div> that has no display:none style.
JavaScript in Firefox cannot see functions in an applet that is inside a div that is not displayed.