7.2 注册表单

用户资料页面已经可以访问了,但内容还不完整。下面我们要为网站创建一个注册表单。如图 5.9图 7.10 所示,“注册”页面还没有什么内容,无法注册新用户。本节会实现如图 7.11 所示的注册表单,添加注册功能。

user show sidebar css 3rd edition图 7.9:添加侧边栏和 CSS 后的用户资料页面new signup page 3rd edition图 7.10:注册页面现在的样子

因为我们要实现通过网页创建用户的功能,那么就把 6.3.4 节在控制台中创建的用户删除吧。最简单的方法是使用 db:migrate:reset 命令:

$ bundle exec rake db:migrate:reset

在某些系统中可能还要重启 Web 服务器才能生效。

signup mockup bootstrap图 7.11:用户注册页面的构思图

7.2.1 使用 form_for

注册页面的核心是一个表单,用于提交注册相关的信息(名字,电子邮件地址,密码和密码确认)。在 Rails 中,创建表单可以使用 form_for 辅助方法,传入 Active Record 对象后,使用该对象的属性构建一个表单。

注册页面的地址是 /signup,由用户控制器的 new 动作处理(代码清单 5.33)。首先,我们要创建传给 form_for 的用户对象,然后赋值给 @user 变量,如代码清单 7.12 所示。

代码清单 7.12:在 new 动作中添加 @user 变量

app/controllers/users_controller.rb

class UsersController < ApplicationController

  def show
    @user = User.find(params[:id])
  end

  def new
 @user = User.new  end
end

表单的代码参见代码清单 7.137.2.2 节会详细分析这个表单,现在我们先添加一些 SCSS,如代码清单 7.14 所示。(注意,这里重用了代码清单 7.2 中的混入。)添加样式后的注册页面如图 7.12 所示。

代码清单 7.13:用户注册表单

app/views/users/new.html.erb

<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>

<div class="row">
 <div class="col-md-6 col-md-offset-3">
  <%= form_for(@user) do |f| %>
  <%= f.label :name %>
  <%= f.text_field :name %>

  <%= f.label :email %>
  <%= f.email_field :email %>

  <%= f.label :password %>
  <%= f.password_field :password %>

  <%= f.label :password_confirmation, "Confirmation" %>
  <%= f.password_field :password_confirmation %>

  <%= f.submit "Create my account", class: "btn btn-primary" %>
  <% end %>
 </div>
</div>
代码清单 7.14:注册表单的样式

app/assets/stylesheets/custom.css.scss

.
.
.
/* forms */

input, textarea, select, .uneditable-input {
  border: 1px solid #bbb;
  width: 100%;
 margin-bottom: 15px;
  @include box_sizing;
}

input {
  height: auto !important;
}

signup form 3rd edition图 7.12:用户注册页面

7.2.2 注册表单的 HTML

为了能更好地理解代码清单 7.13 中定义的表单,可以分成几段来看。我们先看外层结构——开头在 ERb 中调用 form_for 方法,结尾是 end

<%= form_for(@user) do |f| %>
 .
 .
 .
<% end %>

这段代码中有关键字 do,说明 form_for 方法可以接受一个块,而且有一个块变量 f(代表表单)。

我们一般无需了解 Rails 辅助方法的内部实现,但是对于 form_for 来说,我们要知道 f 对象的作用是什么:在这个对象上调用表单字段(例如,文本字段、单选按钮和密码字段)对应的方法,生成的字段元素可以用来设定 @user 对象的属性。也就是说:

<%= f.label :name %>
<%= f.text_field :name %>

生成的 HTML 是一个有标注(label)的文本字段,用来设定用户模型的 name 属性。

在浏览器中按右键,然后选择“审查元素”,会看到页面的源码,如代码清单 7.15 所示。下面花点儿时间介绍一下表单的结构。

代码清单 7.15:图 7.12 中表单的源码
<form accept-charset="UTF-8" action="/users" class="new_user"
      id="new_user" method="post">
  <input name="utf8" type="hidden" value="&#x2713;" />
  <input name="authenticity_token" type="hidden"
         value="NNb6+J/j46LcrgYUC60wQ2titMuJQ5lLqyAbnbAUkdo=" />
  <label for="user_name">Name</label>
  <input id="user_name" name="user[name]" type="text" />

  <label for="user_email">Email</label>
  <input id="user_email" name="user[email]" type="email" />

  <label for="user_password">Password</label>
  <input id="user_password" name="user[password]"
         type="password" />

  <label for="user_password_confirmation">Confirmation</label>
  <input id="user_password_confirmation"
         name="user[password_confirmation]" type="password" />

  <input class="btn btn-primary" name="commit" type="submit"
         value="Create my account" />
</form>

先看表单里的结构。比较一下代码清单 7.13代码清单 7.15,我们发现,下面的 ERb 代码

<%= f.label :name %>
<%= f.text_field :name %>

生成的 HTML 是

<label for="user_name">Name</label>
<input id="user_name" name="user[name]" type="text" />

下面的 ERb 代码

<%= f.label :password %>
<%= f.password_field :password %>

生成的 HTML 是

<label for="user_password">Password</label>
<input id="user_password" name="user[password]" type="password" />

图 7.13 所示,文本字段(type="text")会直接显示填写的内容,而密码字段(type="password")基于安全考虑会遮盖输入的内容。

filled in form bootstrap 3rd edition图 7.13:在表单的文本字段和密码字段中填写内容

7.4 节会介绍,之所以能创建用户,全靠 input 元素的 name 属性:

<input id="user_name" name="user[name]" - - - />
.
.
.
<input id="user_password" name="user[password]" - - - />

7.3 节会介绍,Rails 会以这些 name 属性的值为键,用户输入的内容为值,构成一个名为 params 的哈希,用来创建用户。

另外一个重要的标签是 form。Rails 使用 @user 对象创建这个 form 元素,因为每个 Ruby 对象都知道它所属的类(4.4.1 节),所以 Rails 知道 @user 所属的类是 User,而且,@user 是一个新用户,Rails 知道要使用 post 方法——这正是创建新对象所需的 HTTP 请求(参见旁注 3.2):

<form action="/users" class="new_user" id="new_user" method="post">

这里的 classid 属性并不重要,重要的是 action="/users"method="post"。设定这两个属性后,Rails 会向 /users 发送 POST 请求。接下来的两节会介绍这个请求的效果。

你可能还会注意到,form 标签中有下面这段代码:

<div style="display:none">
  <input name="utf8" type="hidden" value="&#x2713;" />
  <input name="authenticity_token" type="hidden"
         value="NNb6+J/j46LcrgYUC60wQ2titMuJQ5lLqyAbnbAUkdo=" />
</div>

这段代码不会在浏览器中显示,只在 Rails 内部有用,所以你并不需要知道它的作用。简单来说,这段代码首先使用 Unicode 字符 &#x2713;(对号 ✓)强制浏览器使用正确的字符编码提交数据,然后是一个“真伪令牌”(authenticity token),Rails 用它抵御“跨站请求伪造”(Cross-Site Request Forgery,简称 CSRF)攻击。[8]