Create form_for without a changeset

Hi all

I want to use form_for for creating a html form without a changeset.

The plain html form looks as follow:

<form class="ui form sign-in-form">

            <div class="field ui fluid left icon input">
                <input type="text" placeholder="Username" id="sign-in-user" />
                <i class="user icon"></i>
            </div>

            <div class="field ui fluid left icon input">
                <input type="password" placeholder="Password" id="sign-in-pw" />
                <i class="lock icon"></i>
            </div>

            <input class="fluid ui blue button" type="submit" value="Sign In" id="sign-in-submit"/>
        </form>

The function for handling the request:

  def signin(conn, %{"login" => %{"username" => username, "password" => password }}) do
   case Server.sign_in(%UserLogin{username: username, password: password}) do
      {:ok, user = %UserInfo{}} ->
        conn
        |> put_session(:sap_sid, Jwt.encrypt(user))
        |> redirect(to: "/welcome")

      {:error, _} ->
        conn
          |> put_layout(false)
          |> render("index.html")
   end
  end

How can I create a form without a changeset?

Thanks

2 Likes

I belive you can pass a conn struct as first argument to form_for

1 Like

You can also you Phoenix.HTML.Tag.form_tag/3 to create forms, but be aware that you will not be able to use functions from Phoenix.HTML.Form, like the text_input/3 in this case, you will need to build your input tags manually (which normally is not a big problem if you don’t have a model linked to the form).

3 Likes

You can employ parts of Ecto and create tableless model you use in your forms lile it was normal Ecto struct that normally comes from database. Here, somewhere further down the page:
http://blog.plataformatec.com.br/2016/05/ectos-insert_all-and-schemaless-queries/

5 Likes

Let’s assume you have something like this in router.ex
post "/signin", SessionController, :create

You could then create a form as follows using form_for/4

<%= form_for @conn, session_path(@conn, :create), [as: :session], fn f → %>
<%end %>

Where

  1. Plug.Conn is the first param
  2. :create would refer to the create action in a SessionController(for example)
  3. [as: :session] says all fields in form will be collected as conn.params.session

Ref: Phoenix.HTML.Form — Phoenix.HTML v4.0.0

5 Likes

Now I am confuse guys, so different way but which way should I take?

1 Like

If you employ parts of Ecto, you may do normal validations.

If you go with 2nd version that directly passses @conn to form you may need to do validations in some other way.

This is the main difference. The choice is yours - generally there’s always more than one way to do things :smiley:

2 Likes

I choosed the advice from @adides and did as follow:

    <%= form_for @conn, auth_path(@conn, :signin), [as: :sap, method: "post", class: "ui form sign-in-form"], fn f -> %>

        <div class="field ui fluid left icon input">
            <%= text_input f, :username, [id: "sign-in-user", placeholder: "Username"] %>
            <i class="user icon"></i>
        </div>

        <div class="field ui fluid left icon input">
            <%= password_input f, :password, [id: "sign-in-pw", placeholder: "Password"] %>
            <i class="lock icon"></i>
        </div>

        <%= submit "Sign In", [class: "fluid ui blue button", id: "sign-in-submit"] %>

    <% end %>

Thanks so much guys.

6 Likes

I’m quite new to this. Not even an ex Ruby on Rails programmer. This seems to be exactly what I’m looking for, but I’m stumped by one detail. These examples all have an action listed, i.e auth_path(@conn, :signin) in kostonstyle’s solution, session_path(@conn, :create) in adides’ suggestion,… So my noob question is, where does this action function live? I’m trying to understand how this statement “:create would refer to the create action…” relates to the session_path action.

I can understand that I need a create() in the controller, but then in which file in my code does the session_path() code belong in? Thanks!

I stumbled upon this myself.The session_path is automatically generated for you and not actually defined as such. (I grepped the code in all sorts of different ways trying to find it)

It is the resource macros in the router module that creates these helpers for you.

I.e

get “/pages”, PageController, :card_index

Will generate a “page_path” function for you.

See “h(Phoenix.Router)”
or
https://hexdocs.pm/phoenix/1.3.0-rc.1/Phoenix.Router.html

Thanks! I added this to the router:
post “/users/:userName”, UserController, :update, as: :update_page

And now the only place I see update_page_path is:
ganymede:phoenixDSK mbk$ grep -r “update_page_path” *
Binary file _build/dev/lib/phoenixDSK/ebin/Elixir.PhoenixDSK.Router.Helpers.beam

Expected? If so, that means that I reference it in the form but it’s not code to be modified… Thanks again!

Yes this is expected.

The update_path_path is a helper function which you can use in your phoenix application to get the URL for the particular route.

So

update_page_path(@conn, :update, “a-name”)

will produce “/users/a-name”

When the router match on the incoming requested path with the post it will call the funtction UserController.update

The UserController.update function is what you need to implement.

1 Like

Great help! A few things you helped me clarify. 1) The fact that these helper functions produce a string, which is what form_for is looking for in the 2nd position over. I’d not caught that in the docs, even though I clearly see it now! 2) The string is the path describing the action. 3) Finally, thanks for the references to the documentation. This statement in the Router doc “Helpers are automatically generated based on the controller name.” finally made sense. If a controller is named DogcatController, then the helper would be dogcat_path(), where I kept looking at the paths in the router and thinking they determined the name of the helper. Thanks again!

1 Like

Take a look at this video:

1 Like