How to handle loading state with Reactive Record lazy loading - react.rb

I am hoping that someone can point me to the best way to handle loading state in a top level Reactrb component with is lazy loading data through Reactive Record.
My gaol is for the top level component to display a wait state while its children components get the data they need to render and then render the whole component quickly. I don't want individual wait states with child components as this creates a 'jerky' experience when there is a lot of data to load.
If you consider this example:
class PostsWithCommentsList < React::Component::Base
before_mount do
state.posts! Post.all
end
def everything_loaded?
#how do I know when all posts and all comments are loaded?
end
def render
div do
ul do
state.posts.each do |post|
li { post.title }
ul do
post.comments.each do |comment|
li { comment.body }
end
end
end
end if everything_loaded?
end
end
end
How do I get the everything_loaded? method to check that all th Posts and all the Comments have been loaded so the component draws quickly and smoothly?
All help very welcome. Thank you

There is a feature of ReactiveRecord that isn't finished, but is at a point where it can be useful. It's called WhileLoading, and the goal was to be able to say something like:
def render
div do
ul do
state.posts.each do |post|
li { post.title }
ul do
post.comments.each do |comment|
li { comment.body }
end
end
end
end.while_loading do
# Some kind of loading screen
end
end
end
Basically you would call that method on the end of the element you want to have have something shown while it is loading, and then pass in what you want shown. Under the hood this would just show/hide the element instead of not rendering.
Unfortunately, this isn't fully working yet, but here's how it can be used.
after_mount do
#initial_data_loaded = false
Element['.content-loading'].hide
Element['.loading-div'].show
end
def toggle_loading
return if #initial_data_loaded
if ReactiveRecord::WhileLoading.quiet?
Element['.content-loading'].show
Element['.loading-div'].hide
#initial_data_loaded = true
end
end
def render
div do
div.content_loading do
ul do
state.posts.each do |post|
li { post.title }
ul do
post.comments.each do |comment|
li { comment.body }
end
end
end
end
end
div.loading_div(style: { display: 'none' }) do
# Your loading screen
end
end.tap { toggle_loading }
end
It's a little dirty, but that should accomplish what you need for now, until we get WhileLoading working well. Basically the content needs to be rendered for ReactiveRecord to know to fetch and apply that data, so we need to do a show/hide instead of preventing it from rendering until the data is there.

Related

Is is possible to render a normal template html.erb template with active admin?

I have a custom page in my admin built with active admin:
In app/admin/stats.rb:
ActiveAdmin.register_page 'Stats' do
controller do
def index
#foo = 'bar'
end
end
end
And in app/views/admin/stats/index.html.erb:
<h1> Admin stats</h1>
<p><%= #foo %></p>
When I go to /admin/stats, I see my page, but without the normal admin layout (like on the dashboard page)
How can I decorate my page with the default layout ?
I've tried:
ActiveAdmin.register_page 'Stats' do
content do
'foobar'
end
controller do
def index
#foo = 'bar'
end
end
end
But this doesn't change anything. Still my stat page with no layout. Any ideas ?
This is one way to do it:
In app/admin/stats.rb:
ActiveAdmin.register_page 'Stats' do
content do
render 'index'
end
controller do
def index
#foo = 'bar'
end
end
end
And rename app/views/admin/stats/index.html.erb to app/views/admin/stats/_index.html.erb (Notice the _)
And it works fine.
From what I understand, if index.html.erb is present in views/admin/stats, the content block is not called. If index.html.erb is renamed into something else, then we go into the content block, then the layout rendering is called...

how to append/replace alt/title attribute in all image tags?

say I have an action template like this
# home/index.html.erb
<%= img_tag "logo.gif" %>
if I want to add alt/title attribute to it, I can just do
# home/index.html.erb
<%= img_tag "logo.gif", alt: "alt!!", title: "title!!" %>
but I have 1000 image tags and I don't want to change every each one of them.
I then thought of using rack middleware and modify image tags before outputting from server.
http://railscasts.com/episodes/151-rack-middleware?view=asciicast
doc = Nokogiri.HTML(#response.body)
doc.search("img").each do |tag|
[:alt, :title].each{|attribute| tag[attribute] = "changed!!" }
end
but when I follow the railscast episode, it appends entire body on the top of the original rather than replacing it.
Am I doing it wrong in the rack, or is there a smarter way to do this?
Updated answer:
# /config/initializers/image_tag_helper.rb
module ActionView
module Helpers
module AssetTagHelper
def image_tag(source, options={})
options[:src] = path_to_image(source)
options[:alt] = "Default Alt" unless options.has_key?(:alt)
options[:title] = "Default Title" unless options.has_key?(:title)
tag(:img, options)
end
end
end
end
This overrides the image_tag helper method to set default alt and title attributes.

How to customize the will paginate links with images

I want to customize the will paginate links in to dotted images instead of numbers 1,2,3. I tried to customize the css of pagination class but it wasn't worked. Pasted below the links format.
(Existing) Previous 1 2 3 Next
(Need to implement) . . . . . . .
I know this question is old, but perhaps you still want an answer. This is probably a good starting point. Create a helper such as app/helpers/will_paginate_helper.rb with the following content:
module WillPaginateHelper
class DottedLinkRenderer < WillPaginate::ActionView::LinkRenderer
protected
def link(text, target, attributes = {})
if target.is_a? Fixnum
attributes[:rel] = rel_value(target)
target = url(target)
end
attributes[:href] = target
tag(:a, ".", attributes)
end
end
def dotted_will_paginate(collection, options = {})
will_paginate(collection, options.merge(:renderer => WillPaginateHelper::DottedLinkRenderer))
end
end
then in your view use:
<%= dotted_will_paginate #posts %>
This is basically a custom link renderer based on the original link renderer.

Helper function not returning string

So I'd like to generate a random background-color based on an array:
def panel_color
a = ["#E5E0AE","#A4D349","#F1427B","#F09137","#792060"]
return a.sample
end
Simple enough. This is to be used in my disc#index.erb view, so I call it there:
...
<div class="panel" style="background-color: <% panel_color %>;">
...
Since this is a helper method for the view, I placed the function in helpers/disc_helper.rb
module DiscHelper
def panel_color
a = ["#E5E0AE","#A4D349","#F1427B","#F09137","#792060"]
return a.sample
end
end
Which, to my surprise, returns nothing to the view, but does not error, either. I'm thinking I missed something very obvious here, but I'm not quite sure what. Latest rails here.
You're just executing it, not displaying it. Use <%= ... %> instead:
<%= panel_color %>
def panel_color
["#E5E0AE","#A4D349","#F1427B","#F09137","#792060"].sample
end

Rails 3: Will_paginate's .paginate doesn't work

I'm using newes Rails 3 version with will_paginate.
#videos = user.youtube_videos.sort.paginate :page => page
I also added the ##per_page attribute to my youtube_video-model.
But it just won't paginate it. I get always all items in the collection listed.
What have I done wrong?
Yours, Joern.
Why are you calling sort here? That seems unnecessary, and probably would result in it finding all videos and calling pagination on that rather than paying any attention to any variable defined in your Video model. Instead, move the sorting logic into the Video model by using a scope or use the order method.
Here's my solution, my own answer, for all other's having trouble with will_paginate and reading this issue:
Create an ApplicationController method like this:
def paginate_collection(collection, page, per_page)
page_results = WillPaginate::Collection.create(page, per_page, collection.length) do |pager|
pager.replace(collection)
end
collection = collection[(page - 1) * per_page, per_page]
yield collection, page_results
end
Then in your Controller, where you got the collection that should be paginated:
page = setup_page(params[:page]) # see below
#messages = Message.inbox(account)
paginate_collection(#messages, page, Message.per_page) do |collection, page_results|
#messages = collection
#page_results = page_results
end
And in your View:
<% #messages.each do |message| %>
<%# iterate and show message titles or whatever %>
<% end %>
<%= will_paginate #page_results %>
To get the page variable defined, check this:
def setup_page(page)
if !page.nil?
page.to_i
else
1
end
end
So page = setup_page(params[:page]) does the trick, with that simple method.
This WORKS!