Skip to content

Instantly share code, notes, and snippets.

@smpallen99
Last active August 29, 2015 14:15
Show Gist options
  • Save smpallen99/63a85ff2225c6b9c46e1 to your computer and use it in GitHub Desktop.
Save smpallen99/63a85ff2225c6b9c46e1 to your computer and use it in GitHub Desktop.
Elixir form helpers prototype
defmodule ExForm do
@moduledoc """
Prototype for a helpers to generate html forms. Usable with Phoenix.
"""
@doc """
Generates a html form.
## Syntax:
1. pass model name atom and use input_field
form_for :my_model, [url: "/"], fn(f) ->
f
|> input_field(:name, my_model.name)
|> submit("save")
end
2. pass model struct and use input
form_for my_model, [url: "/"], fn(f) ->
f
|> input(:name)
|> submit("save")
end
"""
def form_for(name_or_mode, opts \\ [], fun \\ nil)
def form_for(%{__struct__: model_name} = model, opts, fun) do
_form_for(model, underscore(model_name), opts, fun)
end
def form_for(model_name, opts, fun) do
_form_for(nil, model_name, opts, fun)
end
defp _form_for(model, model_name, opts, fun) do
method = Keyword.get(opts, :method, "post")
url = Keyword.get(opts, :url, "/")
charset = Keyword.get(opts, :charset, "UTF-8")
class = case Keyword.get(opts, :class, nil) do
nil -> " "
other -> " class='#{other}' "
end
data = "<form method='#{method}' accept-charset='#{charset}' " <>
"id='new_#{model_name}'#{class}" <>
"action='#{url}'>\r\n"
form_data = %{model: model, model_name: model_name, data: data, type: :form}
case fun do
nil -> form_data
other ->
other.(form_data)
|> render_form_data
end
end
@doc """
Generate an input field
Generates an input field for the provided form data that includes a the given
model data.
## Syntax
input(form_data, :name, class: "one two", style: "some style")
"""
def input(%{model: model} = form_data, name, type \\ "text", opts \\ []) when model != nil do
input_field(form_data, name, Map.get(model, name, nil), type, opts)
end
def input_field(%{data: data, model_name: model_name} = form_data, name, value, type \\ "text", opts \\ []) do
data = data <> "<input type='#{type}' id='#{model_name}_#{name}' name='#{model_name}[#{name}]' value='#{value}' />\r\n"
Map.put form_data, :data, data
end
@doc """
Generate a hidden input field, given form_data containing a the model
## Syntax
hidden(form_data, :group_id)
"""
def hidden(form_data, name, opts \\ []), do: input(form_data, name, "hidden", opts)
@doc """
Generate a hidden field for form_data that does not include a model
## Syntax
hidden_field(form_data, :group_id, my_model.group_id)
"""
def hidden_field(form_data, name, value, opts \\ []), do: input_field(form_data, name, value, "hidden", opts)
@doc """
Generate a submit button
"""
def submit(%{data: data} = form_data, value \\ "submit", _opts \\ []) do
data = data <> "<input type='submit' name='commit' value='#{value}' />\r\n"
Map.put form_data, :data, data
end
@doc """
Generate the underscored model name, given the model Atom
## Syntax
iex> underscore(MyProject.MyModel)
:my_model
"""
def underscore(module) do
module
|> Atom.to_string
|> String.split(".")
|> List.last
|> Mix.Utils.underscore
end
defp render_form_data(%{type: :form, data: data}) do
Phoenix.HTML.safe data <> "</form>\r\n"
end
end
defmodule MyProject.View do
# ...
@doc """
Generate html for a link
## Syntax
iex> link_to("click me", "/something", class: "link btn", style: "some styling")
{:safe, "<a href='/something' class='link btn' style='some styling'>click me</a>"}
"""
def link_to(name, path, opts \\[]) do
attributes = Keyword.delete(opts, :remote)
|> Enum.reduce("", fn({k,v}, acc) -> acc <> "#{k}='#{v}' " end)
"<a href='#{path}' #{attributes}>#{name}</a>"
|> Phoenix.HTML.safe
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment