Lesson 4 — Rendering from controllers
In traditional Rails, controllers render views implicitly:
|
|
With Phlex, rendering is explicit. The controller passes data directly to the view object:
|
|
Why explicit rendering matters
No implicit instance variables. In ERB, the controller sets @boards and the view accesses it. The coupling is invisible — rename the variable in the controller and the view silently breaks. In Phlex, the view’s initialize method declares exactly what it needs. If the controller passes the wrong data, you get an immediate Literal::TypeError at the point of instantiation.
The view’s interface is documented in code. Views::Boards::Index.new(boards:) tells you everything about what this view needs. No hunting through the controller for instance variables.
Views are testable in isolation. Because data is passed explicitly, you can instantiate and render a view in a test with known data — no controller or request required.
The render call
|
|
Rails understands Phlex views as renderables. The render call works exactly as it does with ERB — it handles response codes, content types, and layout wrapping (which is disabled since our Views::Base handles it via around_template).
Redirects and non-HTML responses
Redirects work exactly as normal — they don’t involve views at all:
|
|
For JSON responses, use render json: as usual. Phlex views are only involved when rendering HTML.
Flash messages
Flash messages need to be read somewhere in the layout. Since our layout is a Phlex component, read them there:
|
|
Exercise
The lesson showed how Phlex eliminates implicit instance variables. To make that concrete, try recreating the ERB anti-pattern in Phlex and see why it fails.
Create a Views::Home::About view and wire it up to a route:
|
|
|
|
|
|
Visit /about. You will see an empty <h1> — @title is nil because Phlex views are plain Ruby objects. The controller’s instance variables are not magically shared with the view.
Now fix it the Phlex way — update the controller to pass title: explicitly and update the view to declare it in initialize. Confirm the heading renders correctly.
Before we do, better check that we’ve included Literal::Properties in our Components::Base class. If it’s not there, add it now:
|
|
Since Views::Base inherits from Components::Base, all views automatically get prop as well — no changes needed there.
Solution
|
|
|
|
You might notice that we didn’t set page_title here so it would default to the value in the AppLayout. We can fix this by adding a method to our Views::Base so that we infer the page_title from the class name. We can always override it if we need to.
Update the Views::Base with this new def page_title method.
|
|
Now visit the /about page and check the browser tab.
Note: As we organise our Base classes you can see how easy it is to add default functionality to our views and components.