请选择 进入手机版 | 继续访问电脑版

技术控

    今日:17| 主题:61562
收藏本版 (1)
最新软件应用技术尽在掌握

[其他] Dynamic forms with Phoenix

[复制链接]
岁月悠长 发表于 2016-9-30 09:35:52
194 2
Today we will learn how to build forms in Phoenix that use our schema information to dynamically show the proper input fields with validations, errors and so on. We aim to support the following API in our templates:
  1. <%= input f, :name %>
  2. <%= input f, :address %>
  3. <%= input f, :date_of_birth %>
  4. <%= input f, :number_of_children %>
  5. <%= input f, :notifications_enabled %>
复制代码
  Each generated input will have the proper markup and classes (we will use Bootstrap in this example), include the proper HTML attributes, such as required for required fields and validations, and show any input error.
  The goal is to build this foundation in our own applications in very few lines of code, without 3rd party dependencies, allowing us to customize and extend it as desired as our application changes.
  Setting up

   Before building our input helper, let’s first generate a new resource which we will use as a template for experimentation (if you don’t have a Phoenix application handy, run mix phoenix.new your_app before the command below):
  1. mix phoenix.gen.html User users name address date_of_birth:datetime number_of_children:integer notifications_enabled:boolean
复制代码
Follow the instructions after the command above runs and then open the form template at “web/templates/user/form.html.eex”. We should see a list of inputs such as:
  1. <div class="form-group">
  2.   <%= label f, :address, class: "control-label" %>
  3.   <%= text_input f, :address, class: "form-control" %>
  4.   <%= error_tag f, :address %>
  5. </div>
复制代码
  The goal is to replace each group above by a single <%= input f, field %> line.
  Adding changeset validations

  Still in the “form.html.eex” template, we can see that a Phoenix form operates on Ecto changesets:
  1. <%= form_for @changeset, @action, fn f -> %>
复制代码
  Therefore, if we want to automatically show validations in our forms, the first step is to declare those validations in our changeset. Open up “web/models/user.ex” and let’s add a couple new validations at the end of the changeset function:
  1. |> validate_length(:address, min: 3)
  2. |> validate_number(:number_of_children, greater_than_or_equal_to: 0)
复制代码
  Also, before we do any changes to our form, let’s start the server with mix phoenix.server and access http://localhost:4000/users/new to see the default form at work.
   Writing the input function

   Now that we have set up the codebase, we are ready to implement the input function.
   The YourApp.InputHelpers module

   Our input function will be defined in a module named YourApp.InputHelpers (where YourApp is the name of your application) which we will place in a new file at “web/views/input_helpers.ex”. Let’s define it:
  1. defmodule YourApp.InputHelpers do
  2.   use Phoenix.HTML
  3.   def input(form, field) do
  4.     "Not yet implemented"
  5.   end
  6. end
复制代码
  Note we used Phoenix.HTML at the top of the module to import the functions from  the Phoenix.HTML project  . We will rely on those functions to build the markup later on.
   If we want our input function to be automatically available in all views, we need to explicitly add it to the list of imports in the “def view” section of our “web/web.ex” file:
  1. import YourApp.Router.Helpers
  2. import YourApp.ErrorHelpers
  3. import YourApp.InputHelpers # Let's add this one
  4. import YourApp.Gettext
复制代码
  With the module defined and properly imported, let’s change our “form.html.eex” function to use the new input functions. Instead of 5 “form-group” divs:
  1. <div class="form-group">
  2.   <%= label f, :address, class: "control-label" %>
  3.   <%= text_input f, :address, class: "form-control" %>
  4.   <%= error_tag f, :address %>
  5. </div>
复制代码
We should have 5 input calls:
  1. <%= input f, :name %>
  2. <%= input f, :address %>
  3. <%= input f, :date_of_birth %>
  4. <%= input f, :number_of_children %>
  5. <%= input f, :notifications_enabled %>
复制代码
Phoenix live-reload should automatically reload the page and we should see “Not yet implemented” appear 5 times.
  Showing the input

   The first functionality we will implement is to render the proper inputs as before. To do so, we will use  the Phoenix.HTML.Form.input_type function  , that receives a form and a field name and returns which input type we should use. For example, for :name , it will return :text_input . For :date_of_birth , it will yield :datetime_select . We can use the returned atom to dispatch to Phoenix.HTML.Form and build our input:
  1. def input(form, field) do
  2.   type = Phoenix.HTML.Form.input_type(form, field)
  3.   apply(Phoenix.HTML.Form, type, [form, field])
  4. end
复制代码
Save the file and watch the inputs appear on the page!
  Wrappers, labels and errors

  Now let’s take the next step and show the label and error messages, all wrapped in a div:
  1. mix phoenix.gen.html User users name address date_of_birth:datetime number_of_children:integer notifications_enabled:boolean0
复制代码
  We used content_tag to build the wrapping div and the existing YourApp.ErrorHelpers.error_tag function that Phoenix generates for every new application that builds an error tag with proper markup.
  Adding Bootstrap classes

  Finally, let’s add some HTML classes to mirror the generated Bootstrap markup:
  1. mix phoenix.gen.html User users name address date_of_birth:datetime number_of_children:integer notifications_enabled:boolean1
复制代码
And that’s it! We are now generating the same markup that Phoenix originally generated. All in 14 lines of code. But we are not done yet, let’s take things to the next level by further customizing our input function.
  Customizing inputs

  Now that we have achieved parity with the markup code that Phoenix generates, we can further extend it and customize it according to our application needs.
  Colorized wrapper

   One useful UX improvement is to, if a form has errors, automatically wrap each field in a success or error state accordingly. Let’s rewrite the wrapper_opts to the following:
  1. mix phoenix.gen.html User users name address date_of_birth:datetime number_of_children:integer notifications_enabled:boolean2
复制代码
  And define the private state_class function as follows:
  1. mix phoenix.gen.html User users name address date_of_birth:datetime number_of_children:integer notifications_enabled:boolean3
复制代码
Now submit the form with errors and you should see every label and input wrapped in green (in case of success) or red (in case of input error).
  Input validations

   We can use  the Phoenix.HTML.Form.input_validations function  to retrieve the validations in our changesets as input attributes and then merge it into our input_opts . Add the following two lines after the input_opts variable is defined (and before the content_tag call):
  1. mix phoenix.gen.html User users name address date_of_birth:datetime number_of_children:integer notifications_enabled:boolean4
复制代码
After the changes above, if you attempt to submit the form without filling the “Address” field, which we imposed a length of 3 characters, the browser won’t allow the form to be submitted. Not everyone is a fan of browser validations and, in this case, you have direct control if you want to include them or not.
   At this point it is worth mentioning both Phoenix.HTML.Form.input_type and Phoenix.HTML.Form.input_validations are defined as part of the Phoenix.HTML.FormData protocol. This means if you decide to use something else besides Ecto changesets to cast and validate incoming data, all of the functionality we have built so far will still work. For those interested in learning more, I recommend checking out  the Phoenix.Ecto project  and learn how the integration between Ecto and Phoenix is done by simply implementing protocols exposed by Phoenix.
  Per input options

   The last change we will add to our input function is the ability to pass options per input. For example, for a given input, we may not want to use the type inflected by input_type . We can add options to handle those cases:
  1. mix phoenix.gen.html User users name address date_of_birth:datetime number_of_children:integer notifications_enabled:boolean5
复制代码
  This means we can now control which function to use from Phoenix.HTML.Form to build our input:
  1. mix phoenix.gen.html User users name address date_of_birth:datetime number_of_children:integer notifications_enabled:boolean6
复制代码
  We also don’t need to be restricted to the inputs supported by Phoenix.HTML.Form . For example, if you want to replace the :datetime_select input that ships with Phoenix by a datepicker, you can wrap the input creation into an function and pattern match on the inputs you want to customize.
   Let’s see how our input functions look like with all the features so far, including support for custom inputs (input validations have been left out):
  1. mix phoenix.gen.html User users name address date_of_birth:datetime number_of_children:integer notifications_enabled:boolean7
复制代码
And then in your template:
  1. mix phoenix.gen.html User users name address date_of_birth:datetime number_of_children:integer notifications_enabled:boolean8
复制代码
Since your application owns the code, you will always have control over the inputs types and how they are customized. Luckily Phoenix ships with enough functionality to give us a head start, without compromising our ability to refine our presentation layer later on.
  Summing up

   This article showed how we can leverage the conveniences exposed in Phoenix.HTML to dynamically build forms using the information we have already specified in our schemas. Although the example above used the User schema, which directly maps to a database table, Ecto 2.0 allows us to use schemas to map to any data source , so the input function can be used for validating search forms, login pages, and so on without changes.
   While we have developed projects such as Simple Form to tackle those problems in our Rails projects, with Phoenix we can get really far using the minimal abstractions that ship as part of the framework, allowing us to get most of the functionality while having full control over the generated markup.
   

Dynamic forms with Phoenix

Dynamic forms with Phoenix-1-技术控-phoenix,the,phoenix,phoenix,os,phoenixsuit,phoenix,marie
不想走啊 发表于 2016-9-30 10:13:00
楼下的不要小看我,我可不是吃素的。
回复 支持 反对

使用道具 举报

姜丽萍 发表于 2016-9-30 11:43:17
火前留名,前排占座,此楼出租,欢迎议价。
回复 支持 反对

使用道具 举报

我要投稿

推荐阅读


回页顶回复上一篇下一篇回列表
手机版/c.CoLaBug.com ( 粤ICP备05003221号 | 文网文[2010]257号 | 粤公网安备 44010402000842号 )

© 2001-2017 Comsenz Inc.

返回顶部 返回列表