Not so time ago I have spent a lot of time thinking about and prototyping new admin panel for phoenix
. No one loves to write admin panels by himself.
But I have a strong feeling that I need to discuss my ideas with community members before reinventing the wheel.
I am also not quite sure that I can handle a project like that by myself, it requires a lot of skills: choosing good code structure, creating good API design. I will highly appreciate any help with it.
I am also looking for guidance from more experienced phoenix
developers.
Existing technologies
I have tried ex_admin
and found it great. I have even contributed to the project. By there are some issues with it and something does not feel right. I can name several things:
- It uses
dsl
, which is pure magic. It is stated to be inspired by âActiveAdminâ (I have never actually seen Ruby on Rails in my life). Butelixir
is not likeruby
. It is hard to reimplement some features. - It uses itâs own tools for almost anything: generating html, routing, etc. It does not follow the same rules and principles which
phoenix
declares. - Source code is very hard to read and modify. It almost impossible to find a bug there. That is caused by custom rich
dsl
and a lot of macro magic. - Frontend is tightly coupled with the backed, in contrast to phoenix-js which is a separate module
Anyway, I am grateful for the great tool I am using on 2 of my projects today.
Ideas
My core idea is: new tool should reuse existing phoenix
-way of doing things, but include some magic to get rid of (or reduce) the boilerplate code.
Routes
I really like routes from phoenix
, so I would like to reuse them. Hereâs a brief example of what seems to me as a reasonable API:
defmodule TestProject.Router do
use TestProject.Web, :router
use PhoenixAdmin.Router
alias TestProject.Accounts.User
# Pipelines:
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_flash
end
pipeline :require_auth do
plug Guardian.Plug.EnsureAuthenticated,
handler: TestProject.AuthController
end
# Scopes:
scope "/admin" do
pipe_through :browser # or `:api` if you need only `json`
pipe_through :require_auth # you can reuse ANY already existing plugs
scope "/user", PhoenixAdmin.CRUD(User) do
# Registering model for our basic CRUD controller,
# note, that we are using the same DSL as Phoenix itself.
resource "/users", UserController, only: [:new, :create, :update, :destroy]
# Maybe we should specify context and context functions to work with the objects via context?
end
end
end
This seems to be a standard way of doing things in phoenix
. Whereâs the magic? Actually, thereâs no magic here. Just pass a regular controller module. It might be even a single controller for both admin and application code. But what to do if that functionality differs?
At first, I thought that passing models and auto-generating controllers and forms from them might be a good idea. Right now I am not so sure.
phoenix
uses generators to create new controllers. Maybe we should do so as well? It offers some very good points:
- Have all your code in front of you. Need to modify something? No problem
- You donât have to learn new
dsl
- It is easy to have single code base for your application and admin panel
- Reuse everything!
How generations should work? What are the reasonable defaults?
Forms, views and templates
I like how phoenix
renders templates and json
. It uses pretty much the same API.
So, letâs reuse it!
We also need to provide some kind of default template: AdminLTE or similar.
And it feels like providing some helper functions via View
to render styled fields (or other useful parts) is pretty useful.
And also maybe something like âdynamic formsâ described here.
It should be possible to include, modify, and extend already existing admin templates and template parts.
Configuration
Admin panels have a lot of configuration. Like (warning, django terms ahead): list_fields
, search_fields
, and so on.
I see it like this:
defmodule AdminTest.Web.UserController do
use AdminTest.Web, :controller
defimpl PhoenixAdmin.Config do
# Here's a configuration for this specific controller:
def list_items(val), do: ...
def search_items(val), do: ...
end
end
Conclusion
Thatâs about it.
What do you think of it? Are there any other ideas?
Would you use such a thing? Or not? Why?
Hereâs a link to the repo: https://github.com/elixir-lang-moscow/phoenix_admin
Hereâs a link to the package (thereâs currently nothing there): https://hex.pm/packages/phoenix_admin
If you would like to contribute to the development, it is more than welcomed. We can continue a discussion on github.
Thanks!