I am conducting partial caching which is working really well.
However if I change the DOM inside the cache block those changes arn't cached. Is there a way to also include those changes?
Here is what I have so far:
<%
cache(#contact.hash_key) do
%><div id="<%=#contact.hash_key%>"></div><%
end
%>
<script>
//use ajax to prepend new messages.
$(document).ready(function(){
$.get("/messages?cid=<%=#contact.hash_key%>", function(data) {
$("#<%=#contact.hash_key%>").prepend(data);
});
});
</script>
UPDATE
Ok so I am trying to attempt to cache the result before it enters the DOM with the same cache key. That way when the cache block is rendered the new data is included with that key.
But I'm not sure the correct way to structure this.
Done!
So for anyone wanting to do the same thing here is a brief outline on how to do it. You may need to make changes or tweak this solution to fit your own.
This will only work if you are using cache keys. In my instance I have 2 types. A contact.hash_key and a message.hash_key
All message caches are kept inside a parent contact cache. So essentially:
<div id='contact-hash'>
<div id='message-hash-1'>
<div id='message-hash-2'>
<div id='message-hash-3'>
First of all we need to loop through the messages and see if they are cached. If they are then you can just render the cached copy. You can do this by using the Rails.cache.read method:
messages.each do |message|
cache = Rails.cache.read 'views/'+message.hash_key
if cache.nil? == false
%><%= cache.html_safe %><%
end
end
So now we have a list of messages from cache thats already been loaded. So what about new messages? Lets load these via ajax so the user isn't waiting for a boring page load.
You will notice in my question above I have querying "/messages?cid=<%=#contact.hash_key%>" in my AJAX call. This is calling the messages controller and rendering the index view.
Before we want to load the rendered view into the DOM we want to write it to the cache first with, surprise surprise, the identical message.hash_key we are going to use to read it.
So in your view:
<%
cache(message[:hash_key]) do
%>
<div class='message'>
This is a new message from the server.
</div>
<%
end
%>
If in some situations this doesn't work (god knows theres so many app permutations out there) you can also use Rails.cache.write 'foo', 'bar' in the controller instead of caching it at the view level.
And there you have it. Now you can add the new hash_key to the list and then render the view back to the DOM as the results of your AJAX call.
Now with the new hash_key in the list you can loop over it and it will come up as a cached copy.
This may or may not be the most elegant solution. If someone wants to simplify or give any advice on it so I can improve it that would be much appreciated.
Related
In my controller, I have this:
#todones = Todo.where(done:true,user:current_user.email).order('updated_at DESC')
In a corresponding view, I have <h3 id="done" class="semi_title"> <%= #todones.size %> tasks done </h3>
And of course, this only gets rendered when the page loads, meaning it doesn't really dynamically count for you.
What I could do is load in the number at the beginning, and add or subtract to that count using Javascript when elements are added or deleted.
I was wondering if there was a more Rails way to approach this though.
I have a Rails app that uses javascript (Backbone) to show user specific data on each users profile page /views/users/show.html.erb. I do this by passing <%= #user.id %> as a data parameter to Backbone's fetch function, however, the only way I know how to get the <%= #user.id %> into Backbone's fetch function is by embedding the javascript in each views/users/show.html.erb page, which therefore allows Backbone to load different user specific info for each views/users/show.html.erb page. Although this works, it seems like the wrong way to do it, as I have read that I should not embed javascript like this. Furthermore, I am going to have to do it a lot, because I wish to display a lot of different kinds of data, more than you see below. So the show.html.erb page will be filled with javascript to make the app work the way I wish.
Question: how might I get #user.id into Backbone's fetch function for each user's show page without embedding javascript in the way that I've done. In the comments, someone suggest I use app/assets/javascripts/user.js, but I don't know how to get <%= #user.id %> into that file. #user.id is readily available in show.html.erb
<script type='text/javascript'>
$(document).ready(function() {
app.collections.awardCollection.fetch({ data: $.param({ user_id: <%= #user.id %> }) }).complete(function(){
app.views.awardCollection = new app.Views.awardCollection({ collection : app.collections.awardCollection});
app.views.awardCollection.render()
});
});
</script>
In order to understand how the views works, is that you can add as many extensions to a view as you want, and they will be parsed by the right library.
If you create a view like
my_view.haml.erb
It will be first parsed with ruby (erb), and then with haml, and will end in a html page.
You can create many views for js, usually you want to archive that when you do ajax, so you can end having a js view like:
my_view.js.erb
First ruby will be parsed (all the <% %> tags), that will end as plain text, and then the server will serve the .js file. But that's usually a common task for ajax.
If you have to render a html page where you want to put some js and you need some ruby code on it, what I usually do is to put the data in the html content with a hidden div.
You can put in any view (even on your layout if you want it to be globally available), something like:
<div id="user_id" style="display: none;"><%= #user.id %></div>
And then on a js (or coffeescript or whatever) you can just check the content of that div:
<script type="text/javascript">
var user_id = $("#user_id").html();
</div>
that's really useful when you want to debug or create tests for your js files, since its plain js and won't throw syntax errors.
I see the comment of Luís Ramalho and Gon is a good option, but I recommend use the following approaches:
If the from the variable is not going to change, print it with <%= %> under .js.erb files located in app/assets/javascripts (note that it will be cached until you restart your app)
If you need server variables the best way is to use Ajax
You can define functions on .js files on app/assets/javascripts and call those functions from the views
If you really don't want any Javascript code in the view, you can create the functions on a .js on app/assets/javascripts (corresponding to the view, for order), and use events and/or store the variables in hidden fields (or even use the data attribute from HTML5)
Is it possible to ignore a line of HTML (in this case it's JS, but that's irrelevant) from a page cache?
To sum it up, I've got a page being cached using caches_page which in some cases the page is viewed with a parameter which sets off a trigger in JS. More specifically, there's an image viewer in there where the JS trigger is an index for the array of the slideshow. So if the param is there with, say, the value of "6" it'll go to the 6th image in the slideshow rather than starting from the beginning. Pretty straightforward stuff.
I would like to cache this page without the offending line of JS ( imgIndex(#) ). Any way to pull that off? If it makes any difference, and I know it might, I'm on Rails 2, not 3. Thanks.
You could use fragments cache instead. That way you can leave the JS and anything you want out of the important stuff.
Something like this:
# your JS and any content you don't wanna cache goes here or after the following block
<% cache do %>
<% #anything here will be cached %>
<% end %>
I am learning Ruby on Rails, and I am very confused on how the controller-model-view relationship works for my application.
What I have now is a table full of comments (posts) users have made. What I want to do is let users click on a comment to see more information in a separate panel (ie, other database fields that weren't initially shown, for example the user_id of the person who posted the comment).
In my _post.html.erb, I have something like:
<div class="post" id="<%= post.post_id %>" onclick = ?? >
<p>post.text</p></div>
What should go in onclick? I need a way for the onclick to call a helper/controller method which can load more information, and then put that in another div on a page (I've tried variations of using the controller and helper to call javascript which inserts html into the site, but that seems messier than it should be). From what I understand, I should create some kind of partial _postdetails.html.erb file that handles the actual displaying of the html, but I have no idea how to specific where that partial would go in the page.
Any help would be appreciated, thanks!
You can achieve what you want either by using Rails helpers or by writing the AJAX calls yourself.
Personally I manually write all my AJAX calls using jQuery.
You can also use Prototype which ships with Rails.
That being said you can do.
In your JS file :
$("div.some-class").click(function()
{
$.ajax(
{
url:"url/to/controller/action",
type:<GET>/<POST>,
data://If you wish to sent any payload
});
});
In your controller :
def some_action
#some computation
render :update do |page|
page["id_of_div_to_be_refreshed"].replace_html :partial => "some_partial"
end
end
I'm wondering if this is a good idea or not, either way I'd like you guys opinion on how to best achieve this sort of messaging popup system I have in my app.
Under some occasions I want to popup a lightbox for users when certain actions are triggered. I already have the lightbox working and opening when when my controller returns JS for a request.
Here is the senario, I want to check if a user has new messages when a new request is made, and if they do I want to show the messages in my lightbox when the new page is loaded.
Should I just put some JS at the bottom of my <body> and render it if the user has messages? Or should I use like flash[:notice] and have it render as JS or something... I'm a bit stuck you see.
Don't use flash notices, this is not what they are for at all. I would have something in the layout like this:
<% if (messages = current_user.new_messages).size > 0 %>
<%= javascript_tag "display_messages(#{messages.collect(&:message_text).inspect})" %>
<% end %>
obviously here i'm guessing at your messages' methods but you get the idea. .inspect will give it an array of strings, you could give it the message data as a json object or whatever.