Rails calling action from view - ruby

Hopefully have a simple question here but I cannot for the life of me seem to find the answer. Just started working with RoR but came from ASP MVC before. I am having an issue rendering partial views whose local variables are not necessarily tied to the variables of the main view. For instance, with a blog I am trying to render a sidebar that will link to the archive.
def sidebar
#blog_posts = Blog.all(:select => "created_at")
#post_months = #blog_posts.group_by { |m| m.created_at.beginning_of_month }
end
The partial view _sidebar is as follows:
<div class="archives">
<h4>Blog Archive</h4>
<% #post_months.sort.reverse.each do |month, posts| %>
<%= link_to "#{h month.strftime("%B %Y")}: #{posts.count}", archive_path(:timeframe => month) %>
<% end %>
</div>
The problem I am having is that if I simply do a render 'sidebar' within my main view the action does not seem to be called and #post_months is always nil. Is it possible to call the action directly from the view and simply have that render 'sidebar'? In ASP MVC I used to just make the sidebar a ChildActionOnly and Render.Action from the mainview, but in RoR I am completely clueless. Any help is appreciated!

I think what's happening here is that yout sidebar is being treated as a partial and your controller method is never being called. In that case I'd put the code currently contained in the sidebar controller method into either the ApplicationHelper module or the helper module of the current view, depending on whether or not you'd need to render the sidebar from other views.
You'd need to adapt the code a bit to work in a module. Rather than setting a session variable you should have the methods return the values you want.
Module SomeModule
def blog_posts
Blog.all :select => "created_at"
end
def post_months
blog_posts.group_by { |m| m.created_at.beginning_of_month }
end
end
Of course, that may very well need to be refactored and might not work as written, but that's the general idea I'd go with.
Good Luck.

Related

How to implement link_to, AJAX, remote: true, respond_to without rendering new url Rails 6

I'm trying to implement adding upvote and downvotes to an app, submit the controller actions remotely using a button or link_to, and refresh a count section with AJAX.
Somehow upvoting or downvoting always redirects to a path of a member. When I use head :no_content, I can't submit the form aka link_to. Having some respond_to do | f |... also just renders the action URL.
Thus, remote: true is kind of not working, as I have another controller using <%= form_for([entry, entry.review], remote: true, :authenticity_token => true) do |f| %> and it works perfectly.
I have tried implementing here, here, here, and this tutorial and this but nothing seems to work.
I'm using the acts_as_votable. Everything works except the routing functionality with AJAX
# routes.rb
resources :entries do
member do
post 'upvote'
post 'unupvote'
post 'downvote'
post 'undownvote'
end
end
# entries_controller.rb
def upvote
#entry.liked_by current_user
end
def unupvote
#entry.unliked_by current_user
end
def downvote
#entry.disliked_by current_user
end
def undownvote
#entry.undisliked_by current_user
end
# entries/index.html.erb
...
<%= button_to upvote_entry_path(entry.id), remote: true,
class: "btn",
id: "upvote-button-#{ entry.id }" do %>
<i class="bi bi-hand-thumbs-up" style="color: #ababab;" id="upvote-<%= entry.id %>"></i>
<small id="upcount-<%= entry.id %>" >
<%= render 'entries/upvote', entry: entry %>
</small>
<% end %>
...
# entries/_upvote.html.erb
<%= entry.get_upvotes.size %>
# entries/upvote.js.erb
$('#upvote-<%= entry.id %>').className('bi bi-hand-thumbs-up-fill');
$('#upvote-<%= entry.id %>').attr('href', '/entries/<%= entry.id %>/unupvote');
$('#upcount-<%= entry.id %>').html('<%=j render 'entries/upvote', entry: entry %>');
Edit, I have changed my link_to to button_to, changed the routes to post
Not sure if your a tag is inside other elements but you might want to consider using a button_to instead of a link_to.
So far the temporary solution, not probably recommended but still does the job programming practice, is hiding and unhiding certain elements on specific conditions.
Upon user interaction (e.g onClick), unhide and hide elements while updating values that resemble the controller update. Simply simulate a refresh and update the form paths/controller actions they point to.
This will mostly work better with boolean check-box forms, likes or dislikes where variables will only alternate between two instances at max. Hence you don't have to write much javascript to handle pseudo outputs.
But still, I'm sure there's a better method for example WebSockets through Action Cable. That can probably be overkill but the closest to realtime updates. respond_to just doesn't seem to work

Rails 4 Close current tab from controller

So, I have this html.erb and this controller (shown below). What I want is to, if simple_captcha.valid? to increment reports, save, AND close current tab. I want to do it from controller, if possible! (And also, would it be a good practice?)
I saw several examples of this done on view page using javascript, but I know nothing of javascript and, if possible, I'd like to deal with it on controller. But, in case I really have to learn javascript to achieve what I want, which direction should I take?
#view (html.erb)
<h4>To report, complete captcha</h4>
<%= show_simple_captcha %>
<%= button_to "report post", create_report_post_path(#forum_post.id) %>
-----------------------------------
#controller
def new_report_post
#forum_post = ForumPost.find(params[:id])
end
def create_report_post
#forum_post = ForumPost.find(params[:id])
if simple_captcha_valid?
#forum_post.reports += 1
#forum_post.save
redirect_to ???
flash[:success] = "Mandou ver."
else
redirect_to report_post_path
flash[:warning] = "Captcha inválido."
end
end
I don't think, you can close a tab, unless that tab was explicitly opened by javascript.
You can refer to this question: link
If however, you are opening the view using javascript. you can send window.close() using a js.erb view.
Instead of redirect_to, it would be something like
respond_to do |format|
format.js { render "js_erb_view" }
end
Inside your js.erb view file,you can send
window.close()
This would only work if you send an ajax request. One of the possible solutions to make this work :)

Rendering partial that belongs to another controller

I've got a menu controller, which is set as my root controller in routes.rb. In my menu view, i try and render the _lights.slim partial with = render :partial => 'lights/lights' but i get the following error: undefined method `lights' for nil:NilClass
MenuController:
class MenuController < ApplicationController
def index
end
end
Menu View (index.slim)
ul.tabs.vertical data-tab=""
li.tab-title.active
a href="#panel1a" Tab 1
.tabs-content.vertical
#panel1a.content.active
= render :partial => 'lights/lights'
LightsController
class LightsController < ApplicationController
before_action :discover_lights
include LIFX
#client = LIFX::Client.lan
#client.discover!
3.times do
#client.lights.refresh
sleep (0.5)
puts "Found #{#client.lights.count} with labels #{#client.lights}"
end
def index
end
def new
end
def light_toggle
light = #client.lights.with_label(params[:label])
light.on? ? light.turn_off : light.turn_on
redirect_to '/'
end
private
def discover_lights
#client = LIFX::Client.lan
#client.discover!
end
end
Lights View (_lights.slim)
h1.subheader LIFX Lights
table.light-table
thead
tr
th Light
th Status
th Power On/Off
th Brightness
tbody
-#client.lights.map do |c|
tr
th #{c.label}
th #{c.power}
th =link_to 'Toggle', light_path(:label => c.label)
th #{c.color.brightness.round(2) * 100}%
end
Routes.rb
root 'menu#index'
get '/lights', to: 'lights#index'
get '/lights/:label', to: 'lights#light_toggle', as: 'light'
I know this is a no brainer, but i'm stuck as to what to do here. I'm thinking it must be an issue with the way that when Menu#Index is called, I never knows about my LightsController, and so #client.blablabla will never make sense. But how will I make my app know about my LightsController when the view is loaded as a partial
Partials
You must appreciate that Partials are not controller-dependent (being stored in a controllers' view directory does not tie them for use with that controller)
This means if you have the functionality to support the partial in another controller, you should be able to use it in different parts of your app
--
Error
This leads us to the identification of the problem you're receiving.
It's not the calling of the partial which causes an issue - it's how you're referring to the code inside it:
undefined method `lights' for nil:NilClass
The error is clearly that you're trying to call the lights method on an object / variable which doesn't exist. This is defined inside the partial itself here:
#client.lights.map do |c|
Therefore, you need to be able to pass the correct data to the partial, enabling it to load the #client object without being dependent on the controller
--
Fix
To do this, you may wish to consider using partial locals -
<%= render partial: "lights/lights", locals: {client: #client} %>
This means that every time you call the partial, you'll have to pass the #client object into the client local var, thus allowing the partial to run controller-independently.
Here's how you'd handle it in the partial itself:
#app/views/lights/_lights.slim
- client.lights.map do |c|

Sinatra App Not Using Erb in Links

The problem I'm having is that Sinatra apps don't always seem to show my ERB when it is used in some specific context of hyperlinks. I have no idea how much is relevant to show for this question, so I'll do the best I can .Basically, I have a file in my Sinatra app called book_show.erb. It has the following line:
<p>Edit Book</p>
However, when that link is rendered in the browser, it links like this:
http://localhost:9292/books/#{#book.id}/edit
The #{#book.id} was not replaced with the actual ID value. There is a #book object and I do use it in other contexts in that very same file. For example:
<h1><%= #book.series %></h2>
<h2><%= #book.title %></h2>
<h3>Timeframe:</h3>
<p><%= #book.timeframe %></p>
I don't even know if my routes would make a difference here for diagnosing this but all of the relevant routes for my books functionality are:
get '/books' do
#title = 'Book Database'
#books = Book.all
erb :books
end
get '/books/new' do
#book = Book.new
erb :book_add
end
get '/books/:id' do
#book = Book.get(params[:id])
erb :book_show
end
post '/books' do
book = Book.create(params[:book])
redirect to("/books/#{book.id}")
end
I don't know what else to show to help diagnose this problem. I'm hoping someone sees something terribly obvious that I'm missing.
Adding a book to the database works just fine; it's only that edit link that I can't get to work correctly -- and that would seem to be pure HTML/ERB. In order to test it, I also added this line to the page:
<p>Testing: <%= "/books/#{#book.id}/edit" %>
That came back and returned this text:
Testing: /books/4/edit
So I know the ID is getting stored. It has to be something to do with the hyperlink but I can find nothing useful on Sinatra that helps at all with this.
ERB template does not behave like a ruby string - you need to explicitly tell it you exit the 'template' part into the 'logic' part. It looks very odd when it comes to attributes:
<p>Edit Book</p>
You could use link_to helpers to make it look better:
link_to('Edit Book', controller: 'books', action: 'edit', id: #book.id)

How to correct in the Sinatra show block

Sorry, I will not use the specific expression in English.
index.erb
<h1>Hello World.</h1>
<ul>
<li>item1</li>
<li>item2</li>
</ul>
<% capture_content :key do %>
I'm Here.
<% end %>
helpers
def capture_content(key, &block)
#content_hash = {}
#content_hash[key] = block.call # this block contains erb all
end
I just want capture_content in content
I hope expression is correct T_T
If you are looking to write yourself the Sinatra equivalent of the Reails content_for helper then you don't need to.
Because there is an extension called Sinatra::ContentFor which is part of the Sinatra::Contrib project which does what you want.
From the documentation:
Sinatra::ContentFor is a set of helpers that allows you to capture
blocks inside views to be rendered later during the request. The most
common use is to populate different parts of your layout from your
view.

Resources