-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathslides_rails_view_and_controller.html
More file actions
582 lines (548 loc) · 31.6 KB
/
slides_rails_view_and_controller.html
File metadata and controls
582 lines (548 loc) · 31.6 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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Slides for
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">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="stylesheets/reset.css">
<link rel="stylesheet" href="stylesheets/reveal.css">
<link rel="stylesheet" href="stylesheets/myslide.css" id="theme">
<link rel="stylesheet" href="stylesheets/code.css">
<script src="javascripts/clipboard.js" data-turbo-track="reload"></script>
<script src="javascripts/slides.js" data-turbo-track="reload"></script>
</head>
<body>
<div class="reveal">
<!-- Any section element inside of this container is displayed as a slide -->
<div class="slides">
<section>
<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>
<p><small>Slides - use arrow keys to navigate, esc to return to page view, f for fullscreen</small></p>
</section>
<section><a class='slide_break' href='rails_view_and_controller.html#slide-0'>▻</a>
<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>
</section>
<section><a class='slide_break' href='rails_view_and_controller.html#slide-1'>▻</a>
<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></section>
<section><a class='slide_break' href='rails_view_and_controller.html#slide-2'>▻</a>
<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></section>
<section><a class='slide_break' href='rails_view_and_controller.html#slide-3'>▻</a>
<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></section>
<section><a class='slide_break' href='rails_view_and_controller.html#slide-4'>▻</a>
<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></section>
<section><a class='slide_break' href='rails_view_and_controller.html#slide-5'>▻</a>
<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></section>
<section><a class='slide_break' href='rails_view_and_controller.html#slide-6'>▻</a>
<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>
</section>
<section><a class='slide_break' href='rails_view_and_controller.html#slide-7'>▻</a>
<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>
</section>
<section><a class='slide_break' href='rails_view_and_controller.html#slide-8'>▻</a>
<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>
</section>
<section><a class='slide_break' href='rails_view_and_controller.html#slide-9'>▻</a>
<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></section>
<section><a class='slide_break' href='rails_view_and_controller.html#slide-10'>▻</a>
<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>
</section>
<section><a class='slide_break' href='rails_view_and_controller.html#slide-11'>▻</a>
<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></section>
<section><a class='slide_break' href='rails_view_and_controller.html#slide-12'>▻</a>
<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></section>
<section><a class='slide_break' href='rails_view_and_controller.html#slide-13'>▻</a>
<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></section>
<section><a class='slide_break' href='rails_view_and_controller.html#slide-14'>▻</a>
<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></section>
<section><a class='slide_break' href='rails_view_and_controller.html#slide-15'>▻</a>
<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></section>
<section><a class='slide_break' href='rails_view_and_controller.html#slide-16'>▻</a>
<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>
</section>
<section><a class='slide_break' href='rails_view_and_controller.html#slide-17'>▻</a>
<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>
</section>
<section><a class='slide_break' href='rails_view_and_controller.html#slide-18'>▻</a>
<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">
</section>
<section><a class='slide_break' href='rails_view_and_controller.html#slide-19'>▻</a>
<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>
</section>
<section><a class='slide_break' href='rails_view_and_controller.html#slide-20'>▻</a>
<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| %>
```
-->
</section>
<section><a class='slide_break' href='rails_view_and_controller.html#slide-21'>▻</a>
<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></section>
</div>
</div>
<!-- End slides. -->
<!-- Required JS files. -->
<script src="javascripts/reveal.js"></script>
<script src="javascripts/search.js"></script>
<script src="javascripts/markdown.js"></script>
<script>
// Also available as an ES module, see:
// https://revealjs.com/initialization/
Reveal.initialize({
controls: false,
progress: true,
center: false,
hash: true,
// The "normal" size of the presentation, aspect ratio will
// be preserved when the presentation is scaled to fit different
// resolutions. Can be specified using percentage units.
width: 1000,
height: 600,
disableLayout: false,
// Factor of the display size that should remain empty around
// the content
margin: 0.05,
// Bounds for smallest/largest possible scale to apply to content
minScale: 0.2,
maxScale: 10.0,
keyboard: {
27: () => {
// do something custom when ESC is pressed
var new_url = window.location.pathname.replace('slides_', '') + window.location.hash.replace('/','slide-');
window.location = new_url;
},
191: 'toggleHelp',
13: 'next', // go to the next slide when the ENTER key is pressed
},
// Learn about plugins: https://revealjs.com/plugins/
plugins: [ RevealSearch, RevealMarkdown ]
});
</script>
</body>
</html>