dynamic page caching in rails - javascript

How to add dynamic caching to time or I should use fragment caching?
I tried to follow RailsCasts but nothing works.
_feed.html.rb
<% cache feed do %>
<div class="feed">
<div id="time">
<%= render 'shared/time' unless #page_caching %>
</div>
<p class="pull-right grey"><%= time_ago_in_words(feed.posted_at) %></p>
<div class="clearfix">
<p class="grey"><%= image_tag feed.profile_pic, :class => 'pic', :size => '15x15' %> #<%= feed.name %></p>
</div>
<p class="content"><%= raw auto_link(feed.content, :username_include_symbol => true, :target => '_blank') %></p>
</div>
<% end %>
_time.html.rb
<p class="pull-right grey"><%= time_ago_in_words(feed.posted_at) %></p>
index.js.rb
$('#feeds').append('<%= j render(#feeds) %>');
<% if #feeds.next_page %>
$('.pagination').replaceWith('<%= j will_paginate(#feeds) %>');
<% else %>
$('.pagination').remove();
<% end %>
$('#time').prepend('<%= j render("shared/time") %>');
feeds_cotroller.rb
def index
#page_caching = true
#feeds = Feed.page(params[:page]).per_page(12)
end

using helpers that changes over time should not be used inside a cache block. The best solution to this is to use a js plugin that will insert the time string for you. google for timeago js plugin and you'll get a bunch of useful results. The first result there is in http://timeago.yarp.com/. Using that plugin, your code should look like the following (i've inserted a script which should insert the time string)
<% cache feed do %>
<div class="feed">
<div id="time">
<%= render 'shared/time' unless #page_caching %>
</div>
<p class="pull-right grey timeago" title="<%= feed.posted_at %>"></p>
<script>
jQuery(document).ready(function() { jQuery("p.timeago").timeago() })
</script>
<div class="clearfix">
<p class="grey">
<%= image_tag feed.profile_pic, :class => 'pic', :size => '15x15' %> #<%= feed.name %>
</p>
</div>
<p class="content"><%= raw auto_link(feed.content, :username_include_symbol => true, :target => '_blank') %></p>
</div>
<% end %>
This is a simple implementation. You probably want to move the js call to an asset file so you dont have to call it inside the cache. Anyway, this should give you an idea.

Related

how to combine ruby code with javascript in rails 7

I need to combine ruby code with javascript, to show something which depends on the server's response.
app/views/posts/index.html.erb
<h1 class="text-center m-4 text-success">Posts</h1>
<div id="posts" data-controller="like" data-like-url-value="<%= like_posts_path %>">
<div class="row">
<% #posts.each do |post| %>
<%= render post %>
<% end %>
</div>
</div>
<script>
if(some condition){
console.log("All Posts has been loaded successfully.")
}
</script>
app/controllers/posts_controller.rb
def index
#posts = Post.all.order(id: :desc)
if #posts.length > 0
session[:post] = true
end
end
I have to use the index action session variable in the index template, I don't have any idea about how to combine ruby code with javascript.
The condition is set and can be checked on the server and the view is rendered on the server. That means you can simple use ERB to check the condition and then add a <script> block when it is true. The script block will run on the client.
# in the controller
def index
#posts = Post.all.order(id: :desc)
#post_loaded = #posts.length > 0
end
# in the view
<h1 class="text-center m-4 text-success">Posts</h1>
<div id="posts" data-controller="like" data-like-url-value="<%= like_posts_path %>">
<div class="row">
<% #posts.each do |post| %>
<%= render post %>
<% end %>
</div>
</div>
<% if #post_loaded %>
<script>
console.log("All Posts has been loaded successfully.")
</script>
<% end %>
I have a solution for the above question, Which you have asked.
app/controllers/posts_controller.rb
def index
#posts = Post.all.order(id: :desc)
if #posts.length > 0
session[:post] = "some value"
end
end
app/views/posts/index.html.erb
<h1 class="text-center m-4 text-success">Posts</h1>
<div id="posts" data-controller="like" data-like-url-value="<%= like_posts_path %>">
<div class="row">
<% #posts.each do |post| %>
<%= render post %>
<% end %>
</div>
</div>
<script>
let post = "<%= session[:post] %>";
console.log(post);
</script>
You can also try this; if your application has a lot of code, it will become easier to handle that and improve code quality as well.
app/controllers/posts_controller.rb
def index
#posts = Post.all.order(id: :desc)
if #posts.length > 0
session[:post] = true
end
end
app/views/posts/index.html.erb
<%= javascript_include_tag "my_javascript" %>
<h1 class="text-center m-4 text-success">Posts</h1>
<div id="posts" data-controller="like" data-like-url-value="<%= like_posts_path %>">
<div class="row">
<% #posts.each do |post| %>
<%= render post %>
<% end %>
</div>
</div>
app/javascript/my_javascript.js
if(some condition){
console.log("All Posts has been loaded successfully.")
}

HTML and ejs Tags Messed Up After Save

I am pretty new to coding and learning templating at the moment. I am having trouble with html and ejs tags auto formatting after save.
As you can see in the images below (sorry, cannot embed images on the post yet since I am new here), html and ejs tags get separated after saving the file and it looks ugly. How can I prevent them from separating?
Saving also auto-indents codes after ejs tags... I want to use format on save, but don't want the indentation.
'Files:Associations' setting already has '*.ejs - html'.
'Emmet: Include Languages' setting also has 'ejs - html'.
'Editor: Default Formatter' is set to None.
Extensions I have are 'EditorConfig for VS Code' and 'EJS language support'
Code Before Save:
<body>
<div class="container">
<h1>Browsing The <%= name %> subreddit</h1>
<h2><%= description %></h2>
<p><%= subscribers %> Total Subscribers</p>
<hr>
<% for(let post of posts) { %>
<article>
<p><%= post.title %> - <b><%=post.author %></b></p>
<% if(post.img) { %>
<img src="<%= post.img %>" alt="">
<% } %>
</article>
<% } %>
</div>
</body>
Code After Save:
(for example, the bold tag looks out of place and I also want p and b tags in one line along with the ejs tags)
<body>
<div class="container">
<h1>Browsing The <%= name %> subreddit</h1>
<h2>
<%= description %>
</h2>
<p>
<%= subscribers %> Total Subscribers
</p>
<hr>
<% for(let post of posts) { %>
<article>
<p>
<%= post.title %> - <b>
<%=post.author %>
</b>
</p>
<% if(post.img) { %>
<img src="<%= post.img %>" alt="">
<% } %>
</article>
<% } %>
</div>
</body>
Indentation on Save - After ejs Tags
<%- include('partials/head') %>
<%- include('partials/head') %>
<body>
<%- include('partials/navbar') %>
<h1>Your random number is: <%= num %>
</h1>
<% if (num % 2===0) { %>
<h2>That is an even number!</h2>
<% } else { %>
<h2>That is an odd number!</h2>
<% } %>
<h3>
<%= num % 2===0 ? 'Even' : 'Odd' %>
</h3>
</body>
</html>
BeforeSave
AfterSave
Files: Associations setting
Emmet: Include Languages

Ruby on Rails on rendering with Ajax

I am working on a simple website which has posts in forms of songs, now I have implemented like and dislike feature, but when I click on like/dislike it renders the numbers of likes/dislikes on all posts. And when I reload the page it returns them to normal. Now I would like it to change only the numbers for that particular post, without affecting the numbers on other posts?
My view for posts:
<div class="col-6">
<% for #s in #songs %>
<div class="card border-secondary mb-1">
<div class="card-header">
<div class="col-1">
<%= image_tag User.find(#s.user_id).user_image.url, :size=>"50x50" %>
</div>
<div class="col-11 text-muted">
<a href="/user/<%= User.find(#s.user_id).username %>" class="info-col">
<%= User.find(#s.user_id).username %>
</a>
- <%= #s.created_at.to_formatted_s(:short) %>
</div>
</div>
<div class="card-body">
<h4 class="card-title">
<%= #s.title %>
</h4>
<p class="card-text"><%= simple_format(#s.content) %></p>
</div>
<div class="card-footer text-muted">
<div class="col-12">
<div class="row">
<div class="col-3" id="song_like">
<%= render '/components/song_like' %>
</div>
<div class="col-3" id="song_dislike">
<%= render '/components/song_dislike' %>
</div>
<div class="col-4" id="song_comment">
<%= render '/components/song_comment' %>
</div>
</div>
</div>
</div>
</div>
<% end %>
</div>
The song_like partial has the following code:
<%= link_to like_song_path(#s.id, like: true), remote: true, method: :post do %>
<i class="fa fa-thumbs-o-up fa-lg" aria-hidden="true"></i> &nbsp <span class="songlikes">
<%= #s.thumbs_up_total %>
</span>
<% end %>
The song_dislike partial has the following code:
<%= link_to like_song_path(#s.id, like: false), method: :post, remote: true do %>
<i class="fa fa-thumbs-o-down fa-lg" aria-hidden="true"></i> &nbsp <span class="songdislikes">
<%= #s.thumbs_down_total %>
</span>
<% end %>
And my 'like' controller is like:
def like
#s = Song.find(params[:id])
#like = Like.create(like: params[:like], user: current_user, song: #s)
respond_to do |format|
if #like.valid?
format.html
format.js
else
format.html
format.js
end
end
end
This is how like.js.erb looks like:
$('.songlikes').html("<%= #s.thumbs_up_total %>");
$('.songdislikes').html("<%= #s.thumbs_down_total %>");
Here is the part of routes.rb file:
resources :songs do
member do
post 'like'
end
end
I am assuming there is some issue on rendering or jquery, but can't figure it out. Do You have any instructions?
EDIT: You should remove the redirect_to in your f.html?
This reloads the page - and the js will therefore not work.
An Ajax call is per definition not reloading the page.
If you remove those lines and try the following:
There's no new data in your jQuery.
You need a global attribute, so the view can find the data from your controller.
For example:
#like = ...
Then in your view you can add a local to be rendered:
$("#song_like").load("<%=j render partial: '/components/song_like', locals: {like: #like} %>");
But, you need to change your partial to be a partial. And change the variable to like so it will be rendered in the right way.
Hope it helps!
UPDATE
If you want to hit a specific song you can render it with each and then assign an id to your post's class
For example:
<% #posts.each do |post| %>
<div class="post-content">
... other stuff ...
<div class="likes like-post-<%= post.id %>">
.. number of likes
</div>
<%= link_to "Like", link_route_path(post) %>
</div>
<% end %>
# controller
# So you can use the id in your js view
#id = params[:id]
# view.js
$(".like-post-" + "<%= #id %>").html("<%= #s.thumbs_up_total %>");
Hope the idea helps.
change rendering like this:
<div class="col-3" id="song_like">
<%= render partial: '/components/song_like', locals: {s: #s, like: true} %>
</div>
then in partial:
<%= link_to like_song_path(s.id, like: like), method: :post, remote: true do %>
<i class="fa fa-thumbs-o-up fa-lg" aria-hidden="true"></i> &nbsp <%= s.thumbs_up_total %>
<% end %>
and in controller of like_song
should define
#s= Song.find(params[:id])
like = Like.create(like: params[:like], user: current_user, song: #song)
#like = !like.like # opposite to your current status
rest of the things good and then in like.js.erb
$('#songs_like').html("<%= escape_javascript(render partial: '/components/song_like', locals: { s: #s, like: #like } ) %>")

Javascript and Ruby Sidescrolling

So im working on a RoR app and would appreciate a bit of help with something. In this app, I've got a series of discussions, posted by users, with comments below, also posted by users. In terms of design, i thought it would be cool if each discussion would take up the entire screen with a button to the right of it. pressing this button would cause the view to move and reveal another discussion. Any suggestions about how I would go about doing this? heres the discussion partial I have currently. thanks!
<link href='http://fonts.googleapis.com/css?family=Titillium+Web:400,300' rel='stylesheet' type='text/css'>
<% content_for :script do %>
<%= javascript_include_tag 'hover_content' %>
<% end %>
<% #micropost = Micropost.new %>
<% #micropost.discussion_id = discussion.id %>
<li>
<div class = "intro-bar"><span class = "intro"><%=discussion.intro %></span></div>
<div class = "content-bar">
<span class = "content"><%= discussion.content %></span>
</div>
<input type='button' id='hideshow' value='hide/show'>
</li>
<span class = "timestamp">
Posted <%= time_ago_in_words(discussion.created_at) %> ago.
</span>
<% if signed_in? %>
<div class = "row">
<aside class = "span4">
<section>
<%= form_for(#micropost) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Post a comment" %>
</div>
<%= f.hidden_field :discussion_id%>
<%= f.submit "Break Up", class: "btn btn-large btn-breakup",:name => "break_up" %>
<%= f.submit "Stay Together", class: "btn btn-large btn-staytogether", :name => "stay_together" %>
<% end %>
</section>
</aside>
</div>
<% end %>
<div class = "comments">
<% discussion.microposts.each do |micropost| %>
<div class = 'comment-box'>
<li>
<div class = "comment-pic"></div>
<div class = "post-comment"><%= micropost.content%></div>
</li>
</div>
<% end %>
</div>
Your best bet would be a carousel.
For jQuery: http://sorgalla.com/projects/jcarousel/
(The "Special Examples" section has a text scroller which looks like what you want, but vertical)
For Prototype: http://code.google.com/p/prototype-carousel/

Is it possible to populate a javascript attribute from a records field?

I have a table of venue records which are being displayed on the index page as partials. Is it possible for the .left and .top values in the javascript to be populated by an integer field taken from venue records?
Also, how can I have the script shown run for each partial as it currently only applies to the first partial generated.
Venue partial:
<%= link_to venue do %>
<div class="venue_partial">
<div class="venue_icon">
<%= image_tag venue.venuetype.photo.url(:thumb), :class => 'image' %>
</div>
<span class="venue_partial_name"><%= venue.name %></span>
<span class="venue_area_and_type"><%= venue.venuetype.name %></span>
<span class="venue_area_and_type"><%= venue.area.name %></span>
</div>
<div id="venue_map_icon" style="position:absolute;"></div>
<script>
document.getElementById("venue_map_icon").style.left= "300px";
document.getElementById("venue_map_icon").style.top= "310px";
</script>
<% end %>
Thanks very much for any help its much appreciated!
Yes on both counts. The problem you have now is that you are referencing an element by an ID in javascript, but that ID is not unique, so javascript finds the first instance of this ID and applies the rules to it, not to the rest of your divs. To fix this, you need to use unique ids:
<%= link_to venue do %>
<div class="venue_partial">
<div class="venue_icon">
<%= image_tag venue.venuetype.photo.url(:thumb), :class => 'image' %>
</div>
<span class="venue_partial_name"><%= venue.name %></span>
<span class="venue_area_and_type"><%= venue.venuetype.name %></span>
<span class="venue_area_and_type"><%= venue.area.name %></span>
</div>
<div id="venue_map_icon_<%= venue.id %>" style="position:absolute;"></div>
<script>
document.getElementById("venue_map_icon_<%= venue.id %>").style.left= "<%= venue.left %>px";
document.getElementById("venue_map_icon_<%= venue.id %>").style.top= "<%= venue.top %>px";
</script>
<% end %>
Sure, it just needs to be output just like you do in the HTML:
document.getElementById("venue_map_icon").style.left = "<%= venue.somevalue %>px";

Categories