Rails how to render partial with data form database - ruby-on-rails

i have a view called week.haml and a controller action called def week in which I get all my todos for this week. I want to render a partial called _events.haml inside week.haml which displays all events that week.
I did it like this:
inside week.haml:
= render partial: 'events`
inside _events.haml
%p= 'Events this week:'
#events.each do |event|
# stuff to show events
end
inside the todo controller:
def events
#events = Event.where(some query)
end
But i always get the error message #events not defined. How can i load events into the event partial? and i don't want to do it inside def week if possible...
I also created a route:
resources :todo do
get 'events', action: :events, as: :events
end
The week.haml file works fine, just the partial doesn't work.

It is bad practice to use instance variables in partials, pass locals instead. Instance variables are for views
In your week view
= render partial: 'events`, events: week.events
And in partial _events
events.each do |event|
# stuff to show events
end

If you're in the controller action week, you'll need to get the events there or share #events between the different controller actions. If you're in the same scope (same controller in this case), you could create a private method for getting data that's similar between different views.
class TodoController < ApplicationController
before_action :get_events, only: [:week, :events]
def week
# Stuff for the week view
end
def events
# Stuff for the events view
end
private
def get_events
#events = Event.where(...)
end
end
In this example, the #events variable is available for both actions week and events. The before_action at the top runs get_events to load the #events variable before executing any other code in the controller action. Since it's an instance variable, it can be shared between different methods in the same class. Calling get_events at the top of both week and events would achieve the same result, but a before_action is usually cleaner IMO.
And just to be clear, all of this is assuming that you have separate views events.haml and week.haml. If not, creating a route and a controller method with the name of the partial does nothing. Just load the events in the week controller action and you're good to go! You could essentially think of a controller method as corresponding to a single view (of the same name usually), and a partial is just an extension of that view (and any other views that use the same partial). You load any data you might need for the view or any of its partials in the controller action, and then share whatever's needed with the partial. Instance variables (starting with "#") are shared automatically, but anything else you would need to pass to the partial explicitly.
Partials are meant for things you need to render multiple times (and/or in multiple places). The Rails docs have a pretty thorough section about them. If you're only rendering events for a single week in week.haml, you probably don't even need the partial, unless you want to use it in other views.

Related

Best way to create views based on a theme

I need to create separate view pages for a controller that I have.
I have a CustomerController that, based on the customer I need to create custom pages, these pages have to be stored as a file not from the db.
So normally I would have this:
class CustomerController < ApplicationController
def show
#customer = Customer.find(param["id"])
end
end
And the views would be located at:
/views/customer/show.html.erb
What I want to do is have the view pages like:
/views/customer/1/show.html.erb
/views/customer/2/show.html.erb
/views/customer/3/show.html.erb
How can I reference the specific view pages to load from within the action?
Is there a way to just pass the value 1,2,3 to the view folder path?
You can specify a template to render in the show action. Rails, without you explicitly calling render, will render the template that corresponds to that specific action...so, in your case, it will automatically render customers/show.html.erb (Note: Your controller should be plural...ie CustomersController, not CustomerController)
To override this, you can do something like this
def show
#customer = Customer.find(params[:id])
render "customers/#{#customer.id}/show.html.erb"
end

Semi-global Rails partial

Is there a better way to achieve what I'm going for?
I have a partial in the /views/shared/ folder that has all the fields that are in a form being used to send an email.
A helper method with default options to render said partial (render partial: 'shared/email_fields' locals: locals where locals is a hash of default variables).
A helper method for every form sending an email that calls the above helper method and passes in either a FormBuilder object or a string containing the beginning of the name html attribute.
The problem I'm having: Most of the email forms differ slightly which results in me having to add additional options to the locals hash and I feel like the global partial is becoming bloated. Is there some way of using a global partial in this way such that the partial doesn't become super bloated?
I've thought of having each form completely separate but that's bad for upkeep and DRY. I've thought of passing in the name of a partial to be rendered inside the global partial but some of these forms need the same options and are rendered from different controllers and I wouldn't want to put a bunch of partials that aren't global in the /views/shared/ folder. Right now, I'm just sticking with the bloated global partial.
Any help would be appreciated!
Here's how I do it. This is going to sound weird, but bear with me.
So, I have basically two forms in my applications. For a form that submits via javascript, it looks like this:
#views/shared/_remote_form.html.haml
= form_tag #presenter.form_path,
remote: true,
id: #presenter.form_id,
class: #presenter.form_classes,
data: #presenter.form_data,
method: #presenter.form_method do
.well
= #presenter.form_inner
.form-controls-container
.form-controls-wrapper
= #presenter.form_controls
As you can see, I use presenters. The presenters are instantiated in the relevant controller as a controller variable, so that the presenter is available to the partial. Something like:
class FooController < ApplicationController
def new
#presenter = NewFooFormPresenter.new(self)
render partial: 'shared/remote_form'
end
...
end
You can see that I'm passing in the controller so that the presenter is able to render various parts of the form.
All FormPresenters inherit from FormPresenterBase that has stubbed methods for each of the methods called in the form. Something like this:
class FormPresenterBase
def initialize(controller)
#controller = controller
end
def form_path
root_path
end
def form_id
'bogus-form-id'
end
def form_classes
'something-bogus'
end
def form_inner; end
def form_controls; end
...
end
That let's me bootstrap the form without throwing a bunch of errors all the time. Naturally, that stubbed form won't really work, but that's okay because each FormPresenter will override the stubbed methods with real values. So, something like:
class NewFooFormPresenter < FormPresenterBase
def form_path
new_for_form_path
end
def form_id
'new-foo-form'
end
def form_classes
'something-not-bogus'
end
# The form fields could be unique to this form. Or, I might have a set of common
# fields that I use across multiple forms. I just decide which partial has the
# correct set of fields and render it here.
def form_inner
render partial: 'new_inner_fields'
end
# The controls are also rendered from partials. Here, I want to have an okay
# button and a cancel button. So, I just call the correct partial that
# renders those. I call html_safe on the resultant string so that it renders
# correctly.
def form_controls
[:okay, :cancel].each_with_object("") do |control_sym, to_return|
render partial: "shared/form_widgets/#{control_sym.to_s}_button"
end.html_safe
end
...
end
Of course, I can get tricky with my FormPresenters. If there are families that share common methods, I can either use further inheritance or module inclusion to keep everything DRY.
So, once I have all my basic form widgets (field combinations, controls, etc.) configured as partials, I can just mix and match in my presenter to my heart's delight. And (at least for forms), I basically never have to write another partial for the rest of my life. Whenever I need a new variant, I just spin up a new FormPresenter and customize it to give me the form I desire.
Actually, there's a little bit more to it than all of that, but hopefully this gives you a sense of another way to skin the cat.
An approach is to have a separate partial for each form. Take all of the items the forms have in common and put them in a partial. You can then reference the "common items" partial within your individual form partials. Depending on how your forms are structured, you may have several "common items" partials, but that is okay. The goal is to keep the code organized and DRY.

How can I run a method before redirecting?

In a specific view, is it possible to run a method in rails before redirecting to any page? My goal is to change the "ordering" attribute in my task objects according to their ordering in a list before I redirect.
MORE INFO
Below is a picture of my task_manager. Users can drag tasks to different day divs. Before the user redirects to a new page, I would like to call a method in the view that parses each day div and updates the ordering attribute for my task model.
You can define helper method. Which is a method defined in the controller and can be accessed from the view.
def your_method
...
end
helper_method :current_user
Then in the view you can call it like this:
<% your_method %>
Please provide some more info on what you are trying to do.
Assuming you have a TasksController handling your Task objects, you would have an index action which would render your tasks index view where your tasks are displayed. Before rendering the view you could order the list of tasks.
If you are redirecting however, the #tasks instance variable in your TasksController would not be available to the target page.

Calling REST index action from Dashboard

Hello Everyone I have a model 'Project' in my rails application which is a REST resource.
I also have a non REST controller 'home' with a method 'dashboard'.
I want the dashboard to render all the Projects. This means, I need to call index action on Project. There are two ways I can think of for doing this:
1.
Have a link_to to Project#Index in my dashbaord template, then as both
dashboard and Index share same layout, user can see list of Projects.
However this needs an extra click.
2.
As all other view elements for Dashboard lie in the layout file, I
could manually set the #projects instance in dashboard action and
render the index template.
This also looks like a wrong way to approach things.
What I have two models 'Project' and say 'Cookie' and I want to list them all in my dashboard? How to go about implementing it in the neatest way possible?
class HomeController < ApplicationController
def dashboard
#projects = Project.all
#cookies = Cookie.all
end
end
In your views/home/dashboard you represent those collections as you need.
If you have a partial in your views/projects/ directory, that is currently used within the projects index action to render the project collection, then you can use that view in your dashboard by calling something like:
<%= render partial: 'projects/list', locals: { projects: #projects } %>
Sounds like in your ProjectController's dashboard method you should be calling something like
projects = Project.find_all
then rendering projects in your corresponding view.

How to make a model aware of its controller in Rails?

I am making a Rails application, and i would like to be able use a model object passed to a view to get the URL of some action on this object, like this, for example:
link_to object.public_send(attribute),
{ :controller => object.controller_path,
:action => :show,
:id => object.id }
What would be a good way to do this? Can it be done with a decorator like Draper? Are there some examples online?
Update. I have thought about this and decided that a decorator is not a good place to keep controller information. It is not decorator's responsibility. A decorator should only know to render formatted data with markup. For now i have created a module called Accessor where i try to mix models with controller and routing awareness. I still wonder if there is a better way to do.
If you don't mind having another instance variable on your view, you can implement this using a very simple class (no need for decorators).
class MyRouter
def initialize(controller, object)
#controller = controller
#object = object
end
def url_for(action_name)
controller.url_for(object, :action => action_name)
end
end
On your controllers:
class AController
def edit
#router = MyRouter.new(self, object)
render 'shared_view'
end
end
class BController
def edit
#router = MyRouter.new(self, object)
render 'shared_view'
end
end
And on your shared view:
<%= #router.url_for(:show) # Varies with the controller that rendered the view %>
Of course, this assumes that the controller you want as target is the same controller that renders the view, which might not be true. Still, using this pattern you can accommodate a more complex logic that suits your needs (having multiple Router classes, for instance), without having to change the view.
I've found a very interesting solution in Objects on Rails by Avdi Grimm: Exhibits for REST. In short, his idea is to apply multiple Ruby's SimpleDelegators as decorators with various functions.

Resources