@@ -604,7 +604,20 @@ class ArticlesController < ApplicationController
604
604
``` erb
605
605
<h1>Listing articles</h1>
606
606
607
-
607
+ <table>
608
+ <tr>
609
+ <th>Title</th>
610
+ <th>Text</th>
611
+ </tr>
612
+
613
+ <% @articles.each do |article| %>
614
+ <tr>
615
+ <td><%= article.title %></td>
616
+ <td><%= article.text %></td>
617
+ <td><%= link_to 'Show', article_path(article) %></td>
618
+ </tr>
619
+ <% end %>
620
+ </table>
608
621
```
609
622
610
623
现在访问 < http://localhost:3000/articles > ,会看到已创建的所有文章的列表。
@@ -622,15 +635,15 @@ class ArticlesController < ApplicationController
622
635
623
636
` link_to ` 方法是 Rails 内置的视图辅助方法之一,用于创建基于链接文本和地址的超链接。在这里地址指的是文章列表页面的路径。
624
637
625
- 接下来添加指向其他视图的链接。首先在 ` app/views/articles/index.html.erb ` 文件中添加“New Article”链接,把这个链接放在 <table > 标签之前:
638
+ 接下来添加指向其他视图的链接。首先在 ` app/views/articles/index.html.erb ` 文件中添加“New Article”链接,把这个链接放在 ` <table> ` 标签之前:
626
639
627
640
``` erb
628
641
<%= link_to 'New article', new_article_path %>
629
642
```
630
643
631
644
点击这个链接会打开用于新建文章的表单。
632
645
633
- 接下来在 app/views/articles/new.html.erb 文件中添加返回 index 动作的链接,把这个链接放在表单之后:
646
+ 接下来在 ` app/views/articles/new.html.erb ` 文件中添加返回 ` index ` 动作的链接,把这个链接放在表单之后:
634
647
635
648
``` erb
636
649
<%= form_for :article, url: articles_path do |f| %>
@@ -640,7 +653,7 @@ class ArticlesController < ApplicationController
640
653
<%= link_to 'Back', articles_path %>
641
654
```
642
655
643
- 最后,在 app/views/articles/show.html.erb 模板中添加返回 index 动作的链接,这样用户看完一篇文章后就可以返回文章列表页面了:
656
+ 最后,在 ` app/views/articles/show.html.erb ` 模板中添加返回 ` index ` 动作的链接,这样用户看完一篇文章后就可以返回文章列表页面了:
644
657
645
658
``` erb
646
659
<p>
@@ -656,9 +669,236 @@ class ArticlesController < ApplicationController
656
669
<%= link_to 'Back', articles_path %>
657
670
```
658
671
659
- TIP: 链接到当前控制器的动作时不需要指定 : controller 选项,因为 Rails 默认使用当前控制器。
672
+ TIP: 链接到当前控制器的动作时不需要指定 ` :controller ` 选项,因为 Rails 默认使用当前控制器。
673
+
674
+ TIP: 在开发环境中(默认情况下我们是在开发环境中工作),Rails 针对每个浏览器请求都会重新加载应用,因此对应用进行修改之后不需要重启服务器。
675
+
676
+ ### 添加验证
677
+
678
+ ` app/models/article.rb ` 模型文件简单到只有两行代码:
679
+
680
+ ``` ruby
681
+ class Article < ApplicationRecord
682
+ end
683
+ ```
684
+
685
+ 虽然这个文件中代码很少,但请注意 ` Article ` 类继承自 ` ApplicationRecord ` 类,而 ` ApplicationRecord ` 类继承自 ` ActiveRecord::Base ` 类。正是 ` ActiveRecord::Base ` 类为 Rails 模型提供了大量功能,包括基本的数据库 CRUD 操作(创建、读取、更新、删除)、数据验证,以及对复杂搜索的支持和关联多个模型的能力。
686
+
687
+ Rails 提供了许多方法用于验证传入模型的数据。打开 ` app/models/article.rb ` 文件,像下面这样修改:
688
+
689
+ ``` ruby
690
+ class Article < ApplicationRecord
691
+ validates :title , presence: true ,
692
+ length: { minimum: 5 }
693
+ end
694
+ ```
695
+
696
+ 添加的代码用于确保每篇文章都有标题,并且标题长度不少于 5 个字符。在 Rails 模型中可以验证多种条件,包括字段是否存在、字段是否唯一、字段的格式、关联对象是否存在,等等。关于验证的更多介绍,请参阅[ active\_ record\_ validations.xml] ( active_record_validations.xml#active-record-validations ) 。
697
+
698
+ 现在验证已经添加完毕,如果我们在调用 ` @article.save ` 时传递了无效的文章数据,验证就会返回 ` false ` 。再次打开 ` app/controllers/articles_controller.rb ` 文件,会看到我们并没有在 ` create ` 动作中检查 ` @article.save ` 的调用结果。在这里如果 ` @article.save ` 失败了,就需要把表单再次显示给用户。为此,需要像下面这样修改 ` app/controllers/articles_controller.rb ` 文件中的 ` new ` 和 ` create ` 动作:
699
+
700
+ ``` ruby
701
+ def new
702
+ @article = Article .new
703
+ end
704
+
705
+ def create
706
+ @article = Article .new (article_params)
707
+
708
+ if @article .save
709
+ redirect_to @article
710
+ else
711
+ render ' new'
712
+ end
713
+ end
714
+
715
+ private
716
+ def article_params
717
+ params.require(:article ).permit(:title , :text )
718
+ end
719
+ ```
720
+
721
+ 在上面的代码中,我们在 ` new ` 动作中创建了新的实例变量 ` @article ` ,稍后你就会知道为什么要这样做。
722
+
723
+ 注意在 ` create ` 动作中,当 ` save ` 返回 ` false ` 时,我们用 ` render ` 代替了 ` redirect_to ` 。使用 ` render ` 方法是为了把 ` @article ` 对象回传给 ` new ` 模板。这里渲染操作是在提交表单的这个请求中完成的,而 ` redirect_to ` 会告诉浏览器发起另一个请求。
724
+
725
+ 刷新 < http://localhost:3000/articles/new > ,试着提交一篇没有标题的文章,Rails 会返回这个表单,但这种处理方式没有多大用处,更好的做法是告诉用户哪里出错了。为此需要修改 ` app/views/articles/new.html.erb ` 文件,添加显示错误信息的代码:
726
+
727
+ ``` erb
728
+ <%= form_for :article, url: articles_path do |f| %>
729
+
730
+ <% if @article.errors.any? %>
731
+ <div id="error_explanation">
732
+ <h2>
733
+ <%= pluralize(@article.errors.count, "error") %> prohibited
734
+ this article from being saved:
735
+ </h2>
736
+ <ul>
737
+ <% @article.errors.full_messages.each do |msg| %>
738
+ <li><%= msg %></li>
739
+ <% end %>
740
+ </ul>
741
+ </div>
742
+ <% end %>
743
+
744
+ <p>
745
+ <%= f.label :title %><br>
746
+ <%= f.text_field :title %>
747
+ </p>
748
+
749
+ <p>
750
+ <%= f.label :text %><br>
751
+ <%= f.text_area :text %>
752
+ </p>
753
+
754
+ <p>
755
+ <%= f.submit %>
756
+ </p>
757
+
758
+ <% end %>
759
+
760
+ <%= link_to 'Back', articles_path %>
761
+ ```
762
+
763
+ 上面我们添加了一些代码。我们使用 ` @article.errors.any? ` 检查是否有错误,如果有错误就使用 ` @article.errors.full_messages ` 列出所有错误信息。
764
+
765
+ ` pluralize ` 是 Rails 提供的辅助方法,接受一个数字和一个字符串作为参数。如果数字比 1 大,字符串会被自动转换为复数形式。
766
+
767
+ 在 ` ArticlesController ` 中添加 ` @article = Article.new ` 是因为如果不这样做,在视图中 ` @article ` 的值就会是 ` nil ` ,这样在调用 ` @article.errors.any? ` 时就会抛出错误。
768
+
769
+ TIP: Rails 会自动用 div 包围含有错误信息的字段,并为这些 div 添加 ` field_with_errors ` 类。我们可以定义 CSS 规则突出显示错误信息。
770
+
771
+ 当我们再次访问 < http://localhost:3000/articles/new > ,试着提交一篇没有标题的文章,就会看到友好的错误信息。
772
+
773
+ ![ 出错的表单] ( form_with_errors.png )
774
+
775
+ ### 更新文章
776
+
777
+ 我们已经介绍了 CRUD 操作中的“CR”两种操作,下面让我们看一下“U”操作,也就是更新文章。
660
778
661
- TIP 在开发环境中(默认情况下我们是在开发环境中工作),Rails 针对每个浏览器请求都会重新加载应用,因此对应用进行修改之后不需要重启服务器。
779
+ 第一步要在 ` ArticlesController ` 中添加 ` edit ` 动作,通常把这个动作放在 ` new ` 动作和 ` create ` 动作之间,就像下面这样:
780
+
781
+ ``` ruby
782
+ def new
783
+ @article = Article .new
784
+ end
785
+
786
+ def edit
787
+ @article = Article .find(params[:id ])
788
+ end
789
+
790
+ def create
791
+ @article = Article .new (article_params)
792
+
793
+ if @article .save
794
+ redirect_to @article
795
+ else
796
+ render ' new'
797
+ end
798
+ end
799
+ ```
800
+
801
+ 接下来在视图中添加一个表单,这个表单类似于前文用于新建文章的表单。创建 ` app/views/articles/edit.html.erb ` 文件,添加下面的代码:
802
+
803
+ ``` erb
804
+ <h1>Editing article</h1>
805
+
806
+ <%= form_for :article, url: article_path(@article), method: :patch do |f| %>
807
+
808
+ <% if @article.errors.any? %>
809
+ <div id="error_explanation">
810
+ <h2>
811
+ <%= pluralize(@article.errors.count, "error") %> prohibited
812
+ this article from being saved:
813
+ </h2>
814
+ <ul>
815
+ <% @article.errors.full_messages.each do |msg| %>
816
+ <li><%= msg %></li>
817
+ <% end %>
818
+ </ul>
819
+ </div>
820
+ <% end %>
821
+
822
+ <p>
823
+ <%= f.label :title %><br>
824
+ <%= f.text_field :title %>
825
+ </p>
826
+
827
+ <p>
828
+ <%= f.label :text %><br>
829
+ <%= f.text_area :text %>
830
+ </p>
831
+
832
+ <p>
833
+ <%= f.submit %>
834
+ </p>
835
+
836
+ <% end %>
837
+
838
+ <%= link_to 'Back', articles_path %>
839
+ ```
840
+
841
+ 上面的代码把表单指向了 ` update ` 动作,这个动作稍后我们再来定义。
842
+
843
+ ` method: :patch ` 选项告诉 Rails 使用 ` PATCH ` 方法提交表单。根据 REST 协议,` PATCH ` 方法是** 更新** 资源时使用的 HTTP 方法。
844
+
845
+ ` form_for ` 辅助方法的第一个参数可以是对象,例如 ` @article ` ,` form_for ` 辅助方法会用这个对象的字段来填充表单。如果传入和实例变量(` @article ` )同名的符号(` :article ` ),也会自动产生相同效果,上面的代码使用的就是符号。关于 ` form_for ` 辅助方法参数的更多介绍,请参阅 [ ` form_for ` 的文档] ( http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_for ) 。
846
+
847
+ 接下来在 ` app/controllers/articles_controller.rb ` 文件中创建 ` update ` 动作,把这个动作放在 ` create ` 动作和 ` private ` 方法之间:
848
+
849
+ ``` ruby
850
+ def create
851
+ @article = Article .new (article_params)
852
+
853
+ if @article .save
854
+ redirect_to @article
855
+ else
856
+ render ' new'
857
+ end
858
+ end
859
+
860
+ def update
861
+ @article = Article .find(params[:id ])
862
+
863
+ if @article .update(article_params)
864
+ redirect_to @article
865
+ else
866
+ render ' edit'
867
+ end
868
+ end
869
+
870
+ private
871
+ def article_params
872
+ params.require(:article ).permit(:title , :text )
873
+ end
874
+ ```
875
+
876
+ ` update ` 动作用于更新已有记录,它接受一个散列作为参数,散列中包含想要更新的属性。和之前一样,如果更新文章时发生错误,就需要把表单再次显示给用户。
877
+
878
+ 上面的代码重用了之前为 ` create ` 动作定义的 ` article_params ` 方法。
879
+
880
+ TIP: 不用把所有属性都传递给 ` update ` 方法。例如,调用 ` @article.update(title: 'A new title') ` 时,Rails 只更新 ` title ` 属性而不修改其他属性。
881
+
882
+ 最后,我们想在文章列表中显示指向 ` edit ` 动作的链接。打开 ` app/views/articles/index.html.erb ` 文件,在 ` Show ` 链接后面添加 ` Edit ` 链接:
883
+
884
+ ``` erb
885
+ <table>
886
+ <tr>
887
+ <th>Title</th>
888
+ <th>Text</th>
889
+ <th colspan="2"></th>
890
+ </tr>
891
+
892
+ <% @articles.each do |article| %>
893
+ <tr>
894
+ <td><%= article.title %></td>
895
+ <td><%= article.text %></td>
896
+ <td><%= link_to 'Show', article_path(article) %></td>
897
+ <td><%= link_to 'Edit', edit_article_path(article) %></td>
898
+ </tr>
899
+ <% end %>
900
+ </table>
901
+ ```
662
902
663
903
接着在 ` app/views/articles/show.html.erb ` 模板中添加 ` Edit ` 链接,这样文章页面也有 ` Edit ` 链接了。把这个链接添加到模板底部:
664
904
@@ -821,7 +1061,25 @@ end
821
1061
``` erb
822
1062
<h1>Listing Articles</h1>
823
1063
<%= link_to 'New article', new_article_path %>
824
-
1064
+ <table>
1065
+ <tr>
1066
+ <th>Title</th>
1067
+ <th>Text</th>
1068
+ <th colspan="3"></th>
1069
+ </tr>
1070
+
1071
+ <% @articles.each do |article| %>
1072
+ <tr>
1073
+ <td><%= article.title %></td>
1074
+ <td><%= article.text %></td>
1075
+ <td><%= link_to 'Show', article_path(article) %></td>
1076
+ <td><%= link_to 'Edit', edit_article_path(article) %></td>
1077
+ <td><%= link_to 'Destroy', article_path(article),
1078
+ method: :delete,
1079
+ data: { confirm: 'Are you sure?' } %></td>
1080
+ </tr>
1081
+ <% end %>
1082
+ </table>
825
1083
```
826
1084
827
1085
在上面的代码中,` link_to ` 辅助方法生成“Destroy”链接的用法有点不同,其中第二个参数是具名路由(named route),还有一些选项作为其他参数。` method: :delete ` 和 ` data: { confirm: 'Are you sure?' } ` 选项用于设置链接的 HTML5 属性,这样点击链接后 Rails 会先向用户显示一个确认对话框,然后用 ` delete ` 方法发起请求。这些操作是通过 JavaScript 脚本 ` jquery_ujs ` 实现的,这个脚本在生成应用骨架时已经被自动包含在了应用的布局中(` app/views/layouts/application.html.erb ` )。如果没有这个脚本,确认对话框就无法显示。
0 commit comments