Formex - a form library for Phoenix inspired by Symfony

So this package isn’t for you. Someday I will add possibility of using it without changesets.

3 Likes

I have added the ability to create a collection of forms. Here is an example of use:

Docs

2 Likes

I believe that a form tool for Phoenix would be best if it did not assume the availability of a library like Ecto, because requiring Ecto creates a coupling between the view and the storage layer, which would then make a couple of decoupling refactors much more difficult in the future.

6 Likes

Now is possible to use embedded_schema instead of schema, it means, there is no database requirement (but still Ecto is required).

I know that using embedded schemas in that way is kinda hack, so I’m not proud of it. But it was very easy to implement.

Example:

defmodule App.Registration do
  use App.Web, :model

  embedded_schema do # instead of `schema "table_name"`
    field :email
    field :password
  end
end
defmodule App.RegistrationType do
  use Formex.Type

  def build_form(form) do
    form
    |> add(:email, :text_input, label: "E-mail")
    |> add(:password, :password_input, label: "Password")
    |> add(:save, :submit, label: "Register")
  end
end

Controller

def register(conn, %{"registration" => registration_params}) do
  RegistrationType
  |> create_form(%Registration{}, registration_params)
  |> handle_form # new function
  |> case do
    {:ok, registration} ->
      # do something with the `registration`
    {:error, form} ->
      # display errors
      render(conn, "index.html", form: form)
  end
end
3 Likes

I have published a new version of formex.

Ecto related code is now extracted to a new library - formex_ecto.
Main library doesn’t have ecto dependency and can be used with standard structs.

Also, now is possible to use external validation libraries. For now there is support for Vex and Ecto.Changeset.validate_* (for those who wants to easily migrate old code which used Changeset’s validation :smiley:) . Current list of supported libraries

UPGRADE.md

5 Likes

I don’t like templates and mixing elixir + html either. I want a separate ui, backend should not have knowledge about the client, ui should be usable with another backend (not elixir / phoenix I mean). I want simple maintenance of forms. So I looked for an opensource drag & drop formbuilder, persisting screendefinitions in json. Of course the forms should be rendered runtime (eventually in development mode) from that same json. The idea is not quite new.
I found something usable and reworked the javascript to work with websockets. So it communicates via phoenix channels with the backend now. Form defs are sent in json to the client, form input is sent to the server via the same channel.
More about how I implemented client-side and serverside validations (with lots of advises against, hahaha ) : Code.eval_something .

1 Like

You seem to be getting mostly criticism in this thread for some reason, so let me say thank you. I appreciate the power that comes from a tool like this.

12 Likes

JSON + Websockets would be so 2017 :smile:

I could create ability to return a form created in formex in JSON that would be sent to a client via Websocket.

This is btw a demo of the formbuilder I’m using https://codepen.io/travist/full/xVyMjo/ (it has a link to github).

1 Like

I also come from the PHP/ Symfony world and I really appreciate your port of the form component. I will give it a try in my next project. :slight_smile:

2 Likes

As @brightball said, people seem to be treating this library as a major software architecture statement and criticising it as such (not configured, not enough decoupling, single data source, etc). This is not supposed to be usable for everyone. This is a labor saving device for the common case, and should be treated as such. If one wants to save typing, of course it’s going to be limited, and coupled to the database and all those evil things. But in some cases, that’s exactly what the user wants :slight_smile:

Personally I think this is very useful, and I’d like a complete CRUD layer, like Django Admin, or it’s Flask cousins (FlaskAdmin and FlaskAppBuilder), which which I’m most familiar (all python libraries).

To expand a little on this point, in FlaskAdmin once you’ve defined your User and Post models (approximately the same as Ecto schemas), you can do this:

from flask_admin.contrib.sqla import ModelView

# Initialize your app

admin = Admin(app, name='...', template_mode='bootstrap3')
admin.add_view(ModelView(User, db.session))
admin.add_view(ModelView(Post, db.session))

And get this for free: http://examples.flask-admin.org/sqla/simple/admin/user/

Yes, it’s extremely coupled to your database. Yes, it’s not very easy to customize. Yes, it supports only a single data source. Yes, it requires an ORM where it’s not easy to predict when the DB is queried (not that much harder than Ecto if you know what you’re doing; if you don’t it might be bad). Yes, you don’t have websockets because Python. Yes, route matching in Flask is very hard to predict (really; the author explains the problem with another framework but all that applies to Flask). Yes black magic happens behind the scenes. But with about THREE lines of code I get a functional admin backend.

Formex is not supposed to help you write Facebook (you should use PHP for that, of course), it’s supposed to help you write FlaskAdmin.

7 Likes

If you’re willing to render the forms on the client, maybe VueJS would be a good option. More, if you’re willing to compile the form templates on the client wuth VueJS’s compiler, this could be done. I’m not sure it’s worth it though. For simple forms, you can do most work on the server, and for complex forms you might want to design the whole thing in Javascript.

1 Like

I thought about something that would return form inputs etc. in JSON to use it in Vue/React/Elm
But in my every React app I have max several inputs so why I would use Formex here. My admin panels however are always in simple HTML rendered on backend. Like in the old days :smiley: So I don’t need such JSON functionality yet

1 Like

Would you be interested in working on a super opinionated FlaskAdmin-type admin backend on top of Formex? It’s not too hard, just write some macros that create a controler, a view and a form type based on the schema and possibly some configuration options that overwrite the defaults.

The tricky bit is always how to add some configurability, but I think there’s a place for super simple backend that can eb generated with a couple lines of code.

2 Likes

Sure.

Did you saw ex_admin? Not as good as our project would be because of lack of Formex :smiley: but what if we want to reinvent the wheel? Anyway, I have never tried it

1 Like

Yes, I contributed some ideas to v2.0 (talon) by the same author. @smpallen99 seems to have stopped working on that a while ago and he was going the opposite direction: less magic, more generators. I think it should be the other way around: more magic, fewer generators xD If you find yourself requiring lots of customizations you’ve probably outgrown these kinds of solutions and need to design from scratch.

1 Like

I think such a project should have an API like this:

defmodule MyApp.MyAdmin do
  use SomeMagicHere
  defresource MySchema1, options: [...]
  defresource MySchema2, options: [...]
  defresource MySchema3, options: [...]
  defresource MySchema4, options: [...]
  # ...
end

It would define controllers for the resources (using macros, no code generation). Then you’d just add the stuff to the router.

defmodule MyAppWeb.Router do
  # ...
  require MyApp.MyAdmin
  # ...

  MyApp.MyAdmin.routes "admin/"
  # ...
end

The :options would allow the user to customize some things, and even add extra controller actions. For example:

defresource User, options: [
  extra_actions: [
    change_password: &PasswordController.change_password/2
  ]
]

My idea was to have zero work for the default case (just define the schemas and you’re good to go) and incrementally more work for more advanced use cases. I think that FlaskAdmin or FlaskAppBuilder are good sources of inspiration.

They take tons of options, with varying levels of granularity depening on how much customization you need. You can change the field labels, the order of the fields, etc. They do all of this with class inheritance, of course, but it an be done by merging option dictionaries r keyword lists.

1 Like

Years ago I built a CakePHP based admin backend generator. It worked really well and had CRUD level access control lists down to the form field. We didn’t use generators because we needed that admin interface to change as our application grew as long as we followed the proper database naming rules. You could override those at any point though.

The trickiest bit with configuration really just boiled down to being able to pass a nested map down the function chain to make deep level changes from the highest level when necessary.

With Elixir though, I’d be more concerned about properly leveraging IOLists and I’d probably go with the generator approach as well. Something like the Taggart library I just saw might be a good fit for it.

Ideally, you’d want to be able to specify configuration details for specific templates, then run a mix task to regenerate the proper template code for each one so that it follows the proper compilation process, etc.

1 Like

That doesn’t sound that tricky. It’s just a job for Map.merge/2

Where do IOLists enter the picture? You use Taggart if you want, of course, but you can just generate EEx templates end compile them with Phoenix’s EEx engine, which I think uses IOLists anyway. Those templates could be compiled at macro-expansion time. Is there anything I’m not seeing here?

1 Like

I might have misunderstood what you were going for when you mentioned going away from generators earlier.

1 Like