-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathrails_view_and_controller.html
More file actions
727 lines (702 loc) · 42.2 KB
/
rails_view_and_controller.html
File metadata and controls
727 lines (702 loc) · 42.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
<!doctype html>
<html dir="ltr" lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Rails Views and Controller — Ruby on Rails Guides</title>
<link rel="stylesheet" type="text/css" href="stylesheets/style.css" data-turbo-track="reload">
<link rel="stylesheet" type="text/css" href="stylesheets/print.css" media="print">
<link rel="stylesheet" type="text/css" href="stylesheets/highlight.css" data-turbo-track="reload">
<link rel="icon" href="images/backend-development.svg" sizes="any">
<script src="javascripts/@hotwired--turbo.js" data-turbo-track="reload"></script>
<script src="javascripts/clipboard.js" data-turbo-track="reload"></script>
<script src="javascripts/guides.js" data-turbo-track="reload"></script>
<meta property="og:title" content="Rails Views and Controller — Ruby on Rails Guides" />
<meta name="description" content="Rails Views and ControllerThe Rails View is concerned with displaying (HTML and JSON) output. The controller is concerned with handling incoming requests, and using the model and views to generate a result.After reading this guide you should understand the role of view, controller and routing in rails know which routes and actions are implied by rails resources and be able to adapt the views and controllers generated by the scaffold generator build a form for editing a model use nested resources" />
<meta property="og:description" content="Rails Views and ControllerThe Rails View is concerned with displaying (HTML and JSON) output. The controller is concerned with handling incoming requests, and using the model and views to generate a result.After reading this guide you should understand the role of view, controller and routing in rails know which routes and actions are implied by rails resources and be able to adapt the views and controllers generated by the scaffold generator build a form for editing a model use nested resources" />
<meta property="og:locale" content="en_US" />
<meta property="og:site_name" content="Textbook Backend Developemnt" />
<meta property="og:image" content="images/backend-development.svg" />
<meta property="og:type" content="website" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+Arabic:wght@100..900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Heebo:wght@100..900&family=Noto+Sans+Arabic:wght@100..900&display=swap" rel="stylesheet">
<meta name="theme-color" content="#2e56e9">
</head>
<body class="guide">
<header id="page_header">
<div class="wrapper clearfix">
<nav id="feature_nav">
<div class="header-logo">
<a href="/">Backend Development</a>
</div>
<ul class="nav">
<li><a class="nav-item" id="home_nav" href="/">Home</a></li>
<li class="guides-index guides-index-large">
<a href="index.html" id="guidesMenu" class="guides-index-item nav-item">Index</a>
<div id="guides" class="clearfix" style="display: none;">
<hr />
<dl class="guides-section-container">
<div class="guides-section">
<dt>Ruby on Rails</dt>
<dd><a href="ruby_commandline.html">Ruby Commandline</a></dd>
<dd><a href="rails_database_and_model.html">Models and ActiveRecord</a></dd>
<dd><a href="rails_db.html">Database and Migrations</a></dd>
<dd><a href="rails_associations_and_validations.html">Associations and Validations</a></dd>
<dd><a href="rails_view_and_controller.html">Routing, View and Controller</a></dd>
<dd><a href="rails_authentication.html">Simple Authentication</a></dd>
<dd><a href="assets_and_import_map.html">The Asset Pipeline and Importmaps</a></dd>
<dd><a href="testing.html">Getting started with Testing</a></dd>
<dd><a href="refactoring_rails.html">Refactoring Rails</a></dd>
<dd><a href="deploy-to-paas.html">Deploy to PAAS</a></dd>
<dd><a href="rails_gems.html">Ruby Gems for your Rails Project</a></dd>
<dd><a href="deploying_rails.html">Deploying Rails</a></dd>
</div>
<div class="guides-section">
<dt>Ruby on Rails - Advanced Topics</dt>
<dd><a href="deploy-to-paas.html">Deploy to PAAS</a></dd>
<dd><a href="rest-api.html">REST API</a></dd>
<dd><a href="graphql-api.html">GraphQL API</a></dd>
<dd><a href="rails_websockets.html">Websocket in Rails</a></dd>
<dd><a href="jobs_and_tasks.html">Jobs and Tasks in Rails</a></dd>
<dd><a href="rails_security.html">Rails Security</a></dd>
</div>
<div class="guides-section">
<dt>Overarching Concerns</dt>
<dd><a href="issue.html">Issue Lifecycle</a></dd>
<dd><a href="security.html">Security</a></dd>
<dd><a href="adv_authentication.html">Advanced Authentication</a></dd>
<dd><a href="caching.html">Caching</a></dd>
<dd><a href="advanced_testing.html">Advanced Testing</a></dd>
<dd><a href="internationalization.html">Internationalization (I18n)</a></dd>
<dd><a href="git_rebasing.html">Git Rebasing</a></dd>
</div>
<div class="guides-section">
<dt>Nodes.js</dt>
<dd><a href="node_vs_rails.html">Node vs. Rails</a></dd>
<dd><a href="node_basics.html">Node Basics</a></dd>
<dd><a href="node_websockets.html">Node Websockets</a></dd>
<dd><a href="node_express.html">Node Web App</a></dd>
<dd><a href="node_cluster.html">Scaling Node</a></dd>
</div>
<div class="guides-section">
<dt>Next.js</dt>
<dd><a href="nextjs.html">Next.js</a></dd>
</div>
</dl>
</div>
</li>
<li class="guides-index guides-index-small">
<select class="guides-index-item nav-item">
<option value="index.html">Index</option>
<optgroup label="Ruby on Rails">
<option value="ruby_commandline.html">Ruby Commandline</option>
<option value="rails_database_and_model.html">Models and ActiveRecord</option>
<option value="rails_db.html">Database and Migrations</option>
<option value="rails_associations_and_validations.html">Associations and Validations</option>
<option value="rails_view_and_controller.html">Routing, View and Controller</option>
<option value="rails_authentication.html">Simple Authentication</option>
<option value="assets_and_import_map.html">The Asset Pipeline and Importmaps</option>
<option value="testing.html">Getting started with Testing</option>
<option value="refactoring_rails.html">Refactoring Rails</option>
<option value="deploy-to-paas.html">Deploy to PAAS</option>
<option value="rails_gems.html">Ruby Gems for your Rails Project</option>
<option value="deploying_rails.html">Deploying Rails</option>
</optgroup>
<optgroup label="Ruby on Rails - Advanced Topics">
<option value="deploy-to-paas.html">Deploy to PAAS</option>
<option value="rest-api.html">REST API</option>
<option value="graphql-api.html">GraphQL API</option>
<option value="rails_websockets.html">Websocket in Rails</option>
<option value="jobs_and_tasks.html">Jobs and Tasks in Rails</option>
<option value="rails_security.html">Rails Security</option>
</optgroup>
<optgroup label="Overarching Concerns">
<option value="issue.html">Issue Lifecycle</option>
<option value="security.html">Security</option>
<option value="adv_authentication.html">Advanced Authentication</option>
<option value="caching.html">Caching</option>
<option value="advanced_testing.html">Advanced Testing</option>
<option value="internationalization.html">Internationalization (I18n)</option>
<option value="git_rebasing.html">Git Rebasing</option>
</optgroup>
<optgroup label="Nodes.js">
<option value="node_vs_rails.html">Node vs. Rails</option>
<option value="node_basics.html">Node Basics</option>
<option value="node_websockets.html">Node Websockets</option>
<option value="node_express.html">Node Web App</option>
<option value="node_cluster.html">Scaling Node</option>
</optgroup>
<optgroup label="Next.js">
<option value="nextjs.html">Next.js</option>
</optgroup>
</select>
</li>
</ul>
</nav>
</div>
</header>
<hr class="hide" />
<section id="feature">
<div class="wrapper">
<h1>Rails Views and Controller</h1><p>The Rails View is concerned with displaying (HTML and JSON) output. The
controller is concerned with handling incoming requests,
and using the model and views to generate a result.</p><p>After reading this guide you should</p>
<ul>
<li>understand the role of view, controller and routing in rails</li>
<li>know which routes and actions are implied by rails resources</li>
</ul>
<p>and be able to</p>
<ul>
<li>adapt the views and controllers generated by the scaffold generator</li>
<li>build a form for editing a model</li>
<li>use nested resources</li>
</ul>
<nav id="subCol">
<h3 class="chapter">
<picture>
<!-- Using the `source` HTML tag to set the dark theme image -->
<source
srcset="images/icon_book-close-bookmark-1-wht.svg"
media="(prefers-color-scheme: dark)"
/>
<img src="images/icon_book-close-bookmark-1.svg" alt="Chapter Icon" />
</picture>
Chapters
</h3>
<ol class="chapters">
<li><a href="#mvc-in-rails">MVC in Rails</a></li>
<li><a href="#the-view">The View</a>
<ul>
<li><a href="#layouts-and-views">Layouts and Views</a></li>
<li><a href="#views-with-erb">Views with ERB</a></li>
<li><a href="#links">Links</a></li>
</ul></li>
<li><a href="#the-controller">The Controller</a>
<ul>
<li><a href="#restful-resources">Restful Resources</a></li>
</ul></li>
<li><a href="#form-helpers">Form Helpers</a>
<ul>
<li><a href="#creating-the-form-tag">Creating the Form Tag</a></li>
<li><a href="#creating-input-elements">Creating Input Elements</a></li>
<li><a href="#sending-the-data">Sending the Data</a></li>
<li><a href="#processing-the-data">Processing the Data</a></li>
<li><a href="#handling-and-displaying-errors">Handling and Displaying Errors</a></li>
<li><a href="#using-view-helpers-in-the-console">Using View Helpers in the console</a></li>
</ul></li>
<li><a href="#further-reading">Further reading</a></li>
</ol>
</nav>
<hr>
</div>
</section>
<main id="container">
<div class="wrapper">
<div id="mainCol">
<div class='slide'>
</div>
<div class='slide'>
<p class='slide_break_block'><a class='slide_break' id='slide-0' href='slides_rails_view_and_controller.html#/0'>◻</a></p>
<h2 id="mvc-in-rails"><a class="anchorlink" href="#mvc-in-rails"><span>1</span> MVC in Rails</a></h2><p>The Pattern "Model-View-Controller" has a long history. It has been used in building Graphical User Interfaces (GUIs) since the 1990ies. The general idea is simple:</p>
<ul>
<li>The model represents the state of the application</li>
<li>The view is concerned with showing the Interface</li>
<li>The controller binds the two together</li>
</ul>
</div>
<div class='slide'>
<p class='slide_break_block'><a class='slide_break' id='slide-1' href='slides_rails_view_and_controller.html#/1'>◻</a></p>
<p>In Ruby on Rails we already encounterd the model, or, to be more specific: the models. Several classes that together represent the state of our application.</p><p>Typically we will have one controller per model, and many views per model.</p><p>The controller is concerned with handling HTTP requests for certain URLs. It will set cookies, return HTTP Status Codes for redirection, or call a View to render HTML, XML, JSON, and return that in the HTTP response.</p><p><img src="images/rails-mvc.svg" alt="MVC in Rails"></p></div>
<div class='slide'>
<p class='slide_break_block'><a class='slide_break' id='slide-2' href='slides_rails_view_and_controller.html#/2'>◻</a></p>
<h2 id="the-view"><a class="anchorlink" href="#the-view"><span>2</span> The View</a></h2><p>The View in the Model-View-Controller pattern
is responsible for generating output that will be
displayed to the user in various ways. This means generating
HTML that will later be displayed by a browser, or generating
XML or JSON that will be loaded by another program.</p><p>We will focus on HTML for now. Views that generate XML or JSON
are covered in the chapter on <a href="apis.html">APIs</a>.</p></div>
<div class='slide'>
<p class='slide_break_block'><a class='slide_break' id='slide-3' href='slides_rails_view_and_controller.html#/3'>◻</a></p>
<p>In Ruby on Rails <strong>Embedded Ruby</strong> (ERB) is normally used
as the templating language for HTML. This will
be familiar to you if you have
used templates in other languages or have used PHP embedded in HTML.</p><p>In ERB the ruby is enclosed by <code><%</code> and <code>%></code>:</p><div class="interstitial code">
<pre><code class="highlight plaintext"><p>
Thank you for your oder,
your order number is <%= @order.no %>
</p>
</code></pre>
<button class="clipboard-button" data-clipboard-text="<p>
Thank you for your oder,
your order number is <%= @order.no %>
</p>
">Copy</button>
</div>
<p>Both Designers and Frontend Developers might want to edit
the HTML templates.</p></div>
<div class='slide'>
<p class='slide_break_block'><a class='slide_break' id='slide-4' href='slides_rails_view_and_controller.html#/4'>◻</a></p>
<h3 id="layouts-and-views"><a class="anchorlink" href="#layouts-and-views"><span>2.1</span> Layouts and Views</a></h3><p>The view file itself (e.g. <code>app/views/controller/action.html.erb</code>) will only
contain the HTML that is specific to this view. There is
another file <code>app/views/layouts/application.html.erb</code> that contains
the surrounding code, that stays the same for every page in the app:
head, main navigation, footer.</p><p><img src="images/layout_view.svg" alt="Layouts and Views"></p></div>
<div class='slide'>
<p class='slide_break_block'><a class='slide_break' id='slide-5' href='slides_rails_view_and_controller.html#/5'>◻</a></p>
<p>If you find other parts of your code that you want to reuse
in several views you can extract it into a "partial". An example
is the <code>_form.html.erb</code> partial created by the scaffold: it is
used both by the <code>new.html.erb</code> and the <code>edit.html.erb</code> view.</p><p><img src="images/layout_view_partial.svg" alt="Layouts, Views and Partials"></p></div>
<div class='slide'>
<p class='slide_break_block'><a class='slide_break' id='slide-6' href='slides_rails_view_and_controller.html#/6'>◻</a></p>
<h3 id="views-with-erb"><a class="anchorlink" href="#views-with-erb"><span>2.2</span> Views with ERB</a></h3><p>When it comes to templating systems there are two competing
schools of thought: on the one side there are minimal <strong>logic-less</strong>
templating systems that only offer the inclusion of variable values
and maybe iteration. On the other hand are <strong>full programming
languages</strong> embedded in HTML.</p><p>ERB is an example of the latter: the full power of Ruby is
available inside the template:</p>
<ul>
<li>Instance Variables of the controller (beginning with <code>@</code>) are available in the view</li>
<li><code><% ruby code here %></code> just evaluates the code</li>
<li><code><%= ruby code here %></code> evaluates the code and includes the result</li>
</ul>
</div>
<div class='slide'>
<p class='slide_break_block'><a class='slide_break' id='slide-7' href='slides_rails_view_and_controller.html#/7'>◻</a></p>
<p>You can use all the usual ruby constructs for iteration, conditions, blocks.
Here's an example of a loop:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="o"><</span><span class="n">ul</span><span class="o">></span>
<span class="o"><</span><span class="sx">% @groups.each </span><span class="k">do</span> <span class="o">|</span><span class="n">group</span><span class="o">|</span> <span class="sx">%>
<li></span><span class="o"><</span><span class="sx">%= link_to group.name, group %></li>
<% end %>
</ul>
</span></code></pre>
<button class="clipboard-button" data-clipboard-text="<ul>
<% @groups.each do |group| %>
<li><%= link_to group.name, group %></li>
<% end %>
</ul>
">Copy</button>
</div>
</div>
<div class='slide'>
<p class='slide_break_block'><a class='slide_break' id='slide-8' href='slides_rails_view_and_controller.html#/8'>◻</a></p>
<h3 id="links"><a class="anchorlink" href="#links"><span>2.3</span> Links</a></h3><p>In Rails you do not write links to your own app "by hand". Use helper methods to get the right URLs.
Use <code>rails routes</code> on the command line do find out which URLs exist and helper methods exist:</p><div class="interstitial code">
<pre><code class="highlight shell"><span class="nv">$ </span><span class="nb">rails </span>routes
Prefix Verb URI Pattern
add_user_group PUT /groups/:id/add_user<span class="o">(</span>.:format<span class="o">)</span>
del_user_group PUT /groups/:id/del_user<span class="o">(</span>.:format<span class="o">)</span>
<span class="nb">groups </span>GET /groups<span class="o">(</span>.:format<span class="o">)</span>
POST /groups<span class="o">(</span>.:format<span class="o">)</span>
new_group GET /groups/new<span class="o">(</span>.:format<span class="o">)</span>
edit_group GET /groups/:id/edit<span class="o">(</span>.:format<span class="o">)</span>
group GET /groups/:id<span class="o">(</span>.:format<span class="o">)</span>
PATCH /groups/:id<span class="o">(</span>.:format<span class="o">)</span>
PUT /groups/:id<span class="o">(</span>.:format<span class="o">)</span>
DELETE /groups/:id<span class="o">(</span>.:format<span class="o">)</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="$ rails routes
Prefix Verb URI Pattern
add_user_group PUT /groups/:id/add_user(.:format)
del_user_group PUT /groups/:id/del_user(.:format)
groups GET /groups(.:format)
POST /groups(.:format)
new_group GET /groups/new(.:format)
edit_group GET /groups/:id/edit(.:format)
group GET /groups/:id(.:format)
PATCH /groups/:id(.:format)
PUT /groups/:id(.:format)
DELETE /groups/:id(.:format)
">Copy</button>
</div>
<p>Use the "prefix" from <code>rails routes</code> and add<code>_path</code> or <code>_url</code> to get the path or full URL of the
action.</p>
<ul>
<li><code><%= link_to "Add a User", add_user_group_path %></code> links to the <code>groups#add_user</code> action</li>
<li><code><%= link_to "Show the Object", object %></code> links to the show action of the object</li>
</ul>
</div>
<div class='slide'>
<p class='slide_break_block'><a class='slide_break' id='slide-9' href='slides_rails_view_and_controller.html#/9'>◻</a></p>
<h2 id="the-controller"><a class="anchorlink" href="#the-controller"><span>3</span> The Controller</a></h2><p>The controller is the central part of MVC. An incoming HTTP request
is routed to exactly one controller action that will respond to the request.
The controller then uses the model(s) to load and manipulate the right data,
and finally displays the resulting page by rendering a view.</p></div>
<div class='slide'>
<p class='slide_break_block'><a class='slide_break' id='slide-10' href='slides_rails_view_and_controller.html#/10'>◻</a></p>
<h3 id="restful-resources"><a class="anchorlink" href="#restful-resources"><span>3.1</span> Restful Resources</a></h3><p>Rails uses <a href="https://en.wikipedia.org/wiki/Representational_state_transfer">REST</a> as
a convention for which actions should be available. For example
if you specify in <code>config/routes.rb</code></p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="n">resources</span> <span class="ss">:zombies</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="resources :zombies
">Copy</button>
</div>
<p>This will generate the following mappings (visibile through <code>rails routes</code>):</p><div class="interstitial code">
<pre><code class="highlight plaintext">HTTP
Method URI Pattern Controller Action
GET /zombies zombies_controller def index
POST /zombies zombies_controller def create
GET /zombies/new zombies_controller def new
GET /zombies/:id/edit zombies_controller def edit
GET /zombies/:id zombies_controller def show
PATCH /zombies/:id zombies_controller def update
DELETE /zombies/:id zombies_controller def destroy
</code></pre>
<button class="clipboard-button" data-clipboard-text="HTTP
Method URI Pattern Controller Action
GET /zombies zombies_controller def index
POST /zombies zombies_controller def create
GET /zombies/new zombies_controller def new
GET /zombies/:id/edit zombies_controller def edit
GET /zombies/:id zombies_controller def show
PATCH /zombies/:id zombies_controller def update
DELETE /zombies/:id zombies_controller def destroy
">Copy</button>
</div>
</div>
<div class='slide'>
<p class='slide_break_block'><a class='slide_break' id='slide-11' href='slides_rails_view_and_controller.html#/11'>◻</a></p>
<p>You have already used the scaffold generator which also
adds the necessary views. This way we end up with full CRUD (create, read, update, delete)
capability. For this example:</p><div class="interstitial code">
<pre><code class="highlight plaintext">rails generate scaffold thing title no:integer description start:date
</code></pre>
<button class="clipboard-button" data-clipboard-text="rails generate scaffold thing title no:integer description start:date
">Copy</button>
</div>
<p>we end up with 4 web pages and the following connections:</p><p><img src="images/rest.png" alt="scaffold"></p><p>The scaffold is only meant as a starting point. Always change
the view to better fit your user's needs. But
try to keep the underlying routes the same.</p></div>
<div class='slide'>
<p class='slide_break_block'><a class='slide_break' id='slide-12' href='slides_rails_view_and_controller.html#/12'>◻</a></p>
<h2 id="form-helpers"><a class="anchorlink" href="#form-helpers"><span>4</span> Form Helpers</a></h2><p>When you write a Rails App, you never write <code>form</code>- or <code>input</code>-tags by hand.
You always use form helpers to contruct the HTML for you. You gain a lot of
functionality by using the helpers, but you also need to understand how they work.</p></div>
<div class='slide'>
<p class='slide_break_block'><a class='slide_break' id='slide-13' href='slides_rails_view_and_controller.html#/13'>◻</a></p>
<h3 id="creating-the-form-tag"><a class="anchorlink" href="#creating-the-form-tag"><span>4.1</span> Creating the Form Tag</a></h3><p>Let's look at a very simple edit-form for a resource called user:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="o"><</span><span class="sx">%= form_for(@user) do |f| %>
Uid: <%=</span> <span class="n">f</span><span class="p">.</span><span class="nf">text_field</span> <span class="ss">:uid</span> <span class="o">%></span> <span class="o"><</span><span class="n">br</span><span class="o">></span>
<span class="no">Name</span><span class="p">:</span> <span class="o"><</span><span class="sx">%= f.text_field :name %> <br>
E-Mail: <%=</span> <span class="n">f</span><span class="p">.</span><span class="nf">email_field</span> <span class="ss">:email</span> <span class="o">%></span> <span class="o"><</span><span class="n">br</span><span class="o">></span>
<span class="no">Homepage</span><span class="p">:</span> <span class="o"><</span><span class="sx">%= f.url_field :homepage %> <br>
<%=</span> <span class="n">f</span><span class="p">.</span><span class="nf">submit</span> <span class="sx">%>
<% end %></span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="<%= form_for(@user) do |f| %>
Uid: <%= f.text_field :uid %> <br>
Name: <%= f.text_field :name %> <br>
E-Mail: <%= f.email_field :email %> <br>
Homepage: <%= f.url_field :homepage %> <br>
<%= f.submit %>
<% end %>
">Copy</button>
</div>
<p>The form helper <code>form_for</code> will create the form-tag
and set the action and method according to the REST conventions.
For example if the <code>@user</code> variable contains a user object with id 16,
the resulting form tag will look like this:</p><div class="interstitial code">
<pre><code class="highlight html"><span class="nt"><form</span>
<span class="na">class=</span><span class="s">"edit_user"</span>
<span class="na">id=</span><span class="s">"edit_user_16"</span>
<span class="na">action=</span><span class="s">"/users/16"</span>
<span class="na">accept-charset=</span><span class="s">"UTF-8"</span>
<span class="na">method=</span><span class="s">"post"</span>
<span class="nt">></span>
<span class="nt"><input</span> <span class="na">type=</span><span class="s">"hidden"</span> <span class="na">name=</span><span class="s">"_method"</span> <span class="na">value=</span><span class="s">"patch"</span> <span class="nt">/></span>
...
<span class="nt"></form></span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="<form
class="edit_user"
id="edit_user_16"
action="/users/16"
accept-charset="UTF-8"
method="post"
>
<input type="hidden" name="_method" value="patch" />
...
</form>
">Copy</button>
</div>
<p>The REST conventions say we should use the PATCH method for updating an existing
resource, but this is not available in html forms currently. Current browsers
only support Html forms using <code>GET</code> or <code>POST</code> methods. Rails gets around
this restriction by using a hidden field and some javascript to actually send the HTTP
request with the correct method.</p></div>
<div class='slide'>
<p class='slide_break_block'><a class='slide_break' id='slide-14' href='slides_rails_view_and_controller.html#/14'>◻</a></p>
<h3 id="creating-input-elements"><a class="anchorlink" href="#creating-input-elements"><span>4.2</span> Creating Input Elements</a></h3><div class="interstitial code">
<pre><code class="highlight ruby"><span class="o"><</span><span class="sx">%= form_for(@user) do |f| %>
Uid: <%=</span> <span class="n">f</span><span class="p">.</span><span class="nf">text_field</span> <span class="ss">:uid</span> <span class="o">%></span> <span class="o"><</span><span class="n">br</span><span class="o">></span>
<span class="no">Name</span><span class="p">:</span> <span class="o"><</span><span class="sx">%= f.text_field :name %> <br>
E-Mail: <%=</span> <span class="n">f</span><span class="p">.</span><span class="nf">email_field</span> <span class="ss">:email</span> <span class="o">%></span> <span class="o"><</span><span class="n">br</span><span class="o">></span>
<span class="no">Homepage</span><span class="p">:</span> <span class="o"><</span><span class="sx">%= f.url_field :homepage %> <br>
<%=</span> <span class="n">f</span><span class="p">.</span><span class="nf">submit</span> <span class="sx">%>
<% end %></span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="<%= form_for(@user) do |f| %>
Uid: <%= f.text_field :uid %> <br>
Name: <%= f.text_field :name %> <br>
E-Mail: <%= f.email_field :email %> <br>
Homepage: <%= f.url_field :homepage %> <br>
<%= f.submit %>
<% end %>
">Copy</button>
</div>
<p>The <code>text_field</code>, <code>email_field</code>, <code>url_field</code> helpers create input
fields that are related to the attributes of the user-object.
The fields are set up correctly for both displaying the current value
of the attribute and for editing and overwriting it when the form is
sent in. For example <code><%= f.text_field :name %></code> might be displayed as</p><div class="interstitial code">
<pre><code class="highlight html"><span class="nt"><input</span> <span class="na">type=</span><span class="s">"text"</span> <span class="na">value=</span><span class="s">"Brigitte Jellinek"</span> <span class="na">name=</span><span class="s">"user[name]"</span> <span class="na">id=</span><span class="s">"user_name"</span> <span class="nt">/></span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="<input type="text" value="Brigitte Jellinek" name="user[name]" id="user_name" />
">Copy</button>
</div>
<p>To turn the validation you defined in the model
into html5 <code>require</code> attributes on the form fields
you can install the gem <code>html5_validators</code>.</p></div>
<div class='slide'>
<p class='slide_break_block'><a class='slide_break' id='slide-15' href='slides_rails_view_and_controller.html#/15'>◻</a></p>
<h3 id="sending-the-data"><a class="anchorlink" href="#sending-the-data"><span>4.3</span> Sending the Data</a></h3><p>When you press the submit-button, the data from the form is sent via a HTTP request.
In the development-log file on the server you can see the request coming in and being
routed to the right action:</p><div class="interstitial code">
<pre><code class="highlight plaintext">Started PATCH "/users/16" for ::1 at 2015-11-11 12:47:15 +0100
Processing by UsersController#update as HTML
Parameters: { "user"=>{"uid"=>"4206851", "name"=>"Brigitte Jellinek", "email"=>"", ... }, "id"=>"16"}
</code></pre>
<button class="clipboard-button" data-clipboard-text="Started PATCH "/users/16" for ::1 at 2015-11-11 12:47:15 +0100
Processing by UsersController#update as HTML
Parameters: { "user"=>{"uid"=>"4206851", "name"=>"Brigitte Jellinek", "email"=>"", ... }, "id"=>"16"}
">Copy</button>
</div>
<p>Actually the Parameters came in through two separate channels: the data for the
user came through the body of the HTTP request, the <code>id</code> came in as part of the URL.
Observe the URI Pattern in the output of <code>rails routes</code>:</p><div class="interstitial code">
<pre><code class="highlight plaintext"> Prefix Verb URI Pattern Controller#Action
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
</code></pre>
<button class="clipboard-button" data-clipboard-text=" Prefix Verb URI Pattern Controller#Action
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
">Copy</button>
</div>
<p>Because the pattern is <code>/users/:id</code> a request to <code>/users/16</code> will also
set the id to 16.</p></div>
<div class='slide'>
<p class='slide_break_block'><a class='slide_break' id='slide-16' href='slides_rails_view_and_controller.html#/16'>◻</a></p>
<h3 id="processing-the-data"><a class="anchorlink" href="#processing-the-data"><span>4.4</span> Processing the Data</a></h3><p>The parameters from the HTTP request can be used
directly in the controller via the <code>params</code> Hash:</p><div class="interstitial code">
<pre><code class="highlight plaintext"># PATCH /users/1
# PATCH /users/1.json
def update
@user = User.find(params[:id])
...
end
</code></pre>
<button class="clipboard-button" data-clipboard-text="# PATCH /users/1
# PATCH /users/1.json
def update
@user = User.find(params[:id])
...
end
">Copy</button>
</div>
</div>
<div class='slide'>
<p class='slide_break_block'><a class='slide_break' id='slide-17' href='slides_rails_view_and_controller.html#/17'>◻</a></p>
<p>For mass-assignments (update, create) rails offers an easy
way to filter out only the parameters you really want to be changed / created,
and ignore all others. The following code will look for a key <code>user</code> in the
params Hash and only pick out its sub-entries <code>uid</code>, <code>name</code> and <code>email</code>:</p><div class="interstitial code">
<pre><code class="highlight plaintext"># PATCH /users/1
# PATCH /users/1.json
def update
@user = User.find(params[:id])
good_params = params.require(:user).permit(:uid, :name, :email)
@user.update(good_params)
redirect_to @user, notice: 'User updated.'
end
</code></pre>
<button class="clipboard-button" data-clipboard-text="# PATCH /users/1
# PATCH /users/1.json
def update
@user = User.find(params[:id])
good_params = params.require(:user).permit(:uid, :name, :email)
@user.update(good_params)
redirect_to @user, notice: 'User updated.'
end
">Copy</button>
</div>
<p>We should also handle errors. If the update does not succeed, <code>update</code> returns <code>false</code>
and the <code>errors</code> attribute is set on the user-object. In this case we just re-display
the edit-view from before:</p><div class="interstitial code">
<pre><code class="highlight plaintext"># PATCH /users/1
# PATCH /users/1.json
def update
@user = User.find(params[:id])
good_params = params.require(:user).permit(:uid, :name, :email)
if @user.update(good_params)
redirect_to @user, notice: 'User was successfully updated.'
else
render :edit
end
end
</code></pre>
<button class="clipboard-button" data-clipboard-text="# PATCH /users/1
# PATCH /users/1.json
def update
@user = User.find(params[:id])
good_params = params.require(:user).permit(:uid, :name, :email)
if @user.update(good_params)
redirect_to @user, notice: 'User was successfully updated.'
else
render :edit
end
end
">Copy</button>
</div>
</div>
<div class='slide'>
<p class='slide_break_block'><a class='slide_break' id='slide-18' href='slides_rails_view_and_controller.html#/18'>◻</a></p>
<h3 id="handling-and-displaying-errors"><a class="anchorlink" href="#handling-and-displaying-errors"><span>4.5</span> Handling and Displaying Errors</a></h3><p>When displaying the form we always display errors that are
available throught the <code>errors</code> attribute of the user object:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="o"><</span><span class="sx">%= form_for(@user) do |f| %>
<% if @user.errors.any? %>
<div id=</span><span class="s2">"error_explanation"</span><span class="o">></span>
<span class="o"><</span><span class="n">h2</span><span class="o">><</span><span class="sx">%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% @user.errors.full_messages.each do |message| %>
<li><%=</span> <span class="n">message</span> <span class="sx">%></li></span>
<span class="o"><</span><span class="sx">% end </span><span class="o">%></span>
<span class="o"><</span><span class="sr">/ul>
</</span><span class="n">div</span><span class="o">></span>
<span class="o"><</span><span class="sx">% end </span><span class="o">%></span>
<span class="o">...</span>
<span class="o"><</span><span class="sx">% end </span><span class="o">%></span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="<%= form_for(@user) do |f| %>
<% if @user.errors.any? %>
<div id="error_explanation">
</div>
<div class='slide'>
<p class='slide_break_block'><a class='slide_break' id='slide-19' href='slides_rails_view_and_controller.html#/19'>◻</a></p>
<h2><%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% @user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
...
<% end %>
">Copy</button>
</div>
</div>
<div class='slide'>
<p class='slide_break_block'><a class='slide_break' id='slide-20' href='slides_rails_view_and_controller.html#/20'>◻</a></p>
<h3 id="using-view-helpers-in-the-console"><a class="anchorlink" href="#using-view-helpers-in-the-console"><span>4.6</span> Using View Helpers in the console</a></h3><p>You already know that
the rails console is great for working with models.
You can also use it to check path helpers, by calling them
through the object <code>app.</code>.</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="o">></span> <span class="n">app</span><span class="p">.</span><span class="nf">projects_path</span>
<span class="o">=></span> <span class="s2">"/projects"</span>
<span class="o">></span> <span class="n">app</span><span class="p">.</span><span class="nf">project_path</span><span class="p">(</span><span class="no">Project</span><span class="p">.</span><span class="nf">first</span><span class="p">)</span>
<span class="o">=></span> <span class="s2">"/projects/2007-portfolio-system-multimediaart-at"</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="> app.projects_path
=> "/projects"
> app.project_path(Project.first)
=> "/projects/2007-portfolio-system-multimediaart-at"
">Copy</button>
</div>
<!--
Nested Resources
-------
* one model belongs to another, and is dependent
* `has_many :cards, :dependent => :destroy`
* makes sense to nest urls:
* /boards/3/cards - all the cards in board 3
### model:
* `has_many :cards, :dependent => :destroy`
### routes file:
``` ruby
resources :boards do
resources :cards
end
```
### Routes constructed by nested resources
``` sh
GET /boards
POST /boards
GET /boards/new
GET /boards/:id/edit
GET /boards/:id
PUT /boards/:id
DELETE /boards/:id
GET /boards/:board_id/cards
POST /boards/:board_id/cards
GET /boards/:board_id/cards/new
GET /boards/:board_id/cards/:id/edit
GET /boards/:board_id/cards/:id
PUT /boards/:board_id/cards/:id
DELETE /boards/:board_id/cards/:id
```
### Changes
If you switch to nested resources you need to change
a lot of links: Instead of `cards_path` you will need `boards_cards_path(board)`.
Another change is needed in the form for editing or creating a card:
``` ruby
<%= form_for [ @board, @card ] do |f| %>
```
-->
</div>
<div class='slide'>
<p class='slide_break_block'><a class='slide_break' id='slide-21' href='slides_rails_view_and_controller.html#/21'>◻</a></p>
<h2 id="further-reading"><a class="anchorlink" href="#further-reading"><span>5</span> Further reading</a></h2>
<ul>
<li>The Rails Guides give a good introduction to the subject:
<ul>
<li>Rails Guide: <a href="https://guides.rubyonrails.org/layouts_and_rendering.html">Layouts and Rendering in Rails</a></li>
<li>Rails Guide: <a href="https://guides.rubyonrails.org/action_controller_overview.html">Action Controller Overview</a></li>
<li>Rails Guide: <a href="https://guides.rubyonrails.org/routing.html">Rails Routing from the Outside In</a></li>
</ul></li>
<li>Use the <a href="https://edgeapi.rubyonrails.org/">Rails API</a> documentation to look up the details:
<ul>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to">link_to</a></li>
<li><a href="https://edgeapi.rubyonrails.org/classes/AbstractController/Callbacks/ClassMethods.html#method-i-before_action">before_action</a></li>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Resources.html">resources</a></li>
</ul></li>
<li>Alternative to ERB: <a href="http://haml.info/tutorial.html">HAML</a> similar to SASS</li>
</ul>
</div>
</div>
</div>
</main>
<hr class="hide" />
<footer id="page_footer">
<div class="wrapper">
<p class="copyright">published under <a href="https://creativecommons.org/licenses/by-nc-sa/3.0/at/deed.de">creative commons by-nc-sa</a> in 2012-2025 by <a href="https://brigitte-jellinek.at">Brigitte Jellinek</a>.
</p>
<p>If you want to contribute: <a href="https://github.com/backend-development/backend-development-textbook/fork">fork the source on github</a>
</p>
<p>Favicon "backend development" by Arkinasi from <a href="https://thenounproject.com/browse/icons/term/backend-development/" target="_blank" title="backend development Icons">Noun Project</a> (CC BY 3.0)</p>
</div>
</footer>
</body>
</html>