-
Notifications
You must be signed in to change notification settings - Fork 6
/
10-comments.md.erb
529 lines (423 loc) · 25.2 KB
/
10-comments.md.erb
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
---
title: ข้อคิดเห็น
slug: comments
complete: 100
date: 0010/01/01
number: 10
contents: แสดงข้อคิดเห็นที่มีอยู่|เพิ่มฟอร์มป้อนข้อคิดเห็น|รู้วิธีโหลดเฉพาะข้อคิดเห็นล่าสุด|ใส่จำนวนข้อคิดเห็นลงในข่าว
paragraphs: 34
---
เป้าหมายของเว็บข่าวสังคมก็คือ การสร้างชุมชนผู้ใช้ ซึ่งจะเป็นเรื่องยากหากไม่มีช่องทางให้ผู้คนพูดคุยกันได้ ดังนั้นในบทนี้เราจะมาเพิ่มข้อคิดเห็นกัน !
เราจะเริ่มด้วยการสร้างคอลเลกชั่นใหม่เพื่อเก็บข้อคิดเห็น และตามด้วยการสร้างข้อมูลพื้นฐานบางส่วนใส่เข้าไปในคอลเลกชั่น
~~~js
Comments = new Mongo.Collection('comments');
~~~
<%= caption "lib/collections/comments.js" %>
~~~js
// Fixture data
if (Posts.find().count() === 0) {
var now = new Date().getTime();
// create two users
var tomId = Meteor.users.insert({
profile: { name: 'Tom Coleman' }
});
var tom = Meteor.users.findOne(tomId);
var sachaId = Meteor.users.insert({
profile: { name: 'Sacha Greif' }
});
var sacha = Meteor.users.findOne(sachaId);
var telescopeId = Posts.insert({
title: 'Introducing Telescope',
userId: sacha._id,
author: sacha.profile.name,
url: 'http://sachagreif.com/introducing-telescope/',
submitted: new Date(now - 7 * 3600 * 1000)
});
Comments.insert({
postId: telescopeId,
userId: tom._id,
author: tom.profile.name,
submitted: new Date(now - 5 * 3600 * 1000),
body: 'Interesting project Sacha, can I get involved?'
});
Comments.insert({
postId: telescopeId,
userId: sacha._id,
author: sacha.profile.name,
submitted: new Date(now - 3 * 3600 * 1000),
body: 'You sure can Tom!'
});
Posts.insert({
title: 'Meteor',
userId: tom._id,
author: tom.profile.name,
url: 'http://meteor.com',
submitted: new Date(now - 10 * 3600 * 1000)
});
Posts.insert({
title: 'The Meteor Book',
userId: tom._id,
author: tom.profile.name,
url: 'http://themeteorbook.com',
submitted: new Date(now - 12 * 3600 * 1000)
});
}
~~~
<%= caption "server/fixtures.js" %>
และก็ต้องไม่ลืมที่จะเผยแพร่และบอกรับข้อมูลให้กับคอลเลกชั่นใหม่นี้ด้วย
~~~js
Meteor.publish('posts', function() {
return Posts.find();
});
Meteor.publish('comments', function() {
return Comments.find();
});
~~~
<%= caption "server/publications.js" %>
<%= highlight "5,6,7" %>
~~~js
Router.configure({
layoutTemplate: 'layout',
loadingTemplate: 'loading',
notFoundTemplate: 'notFound',
waitOn: function() {
return [Meteor.subscribe('posts'), Meteor.subscribe('comments')];
}
});
~~~
<%= caption "lib/router.js" %>
<%= highlight "5~7" %>
<%= commit "10-1", "Added comments collection, pub/sub and fixtures." %>
จำไว้ว่า ถ้าเราต้องการให้โค้ดสร้างข้อมูลชุดนี้ทำงาน เราจำเป็นต้องสั่ง `meteor reset` เพื่อลบข้อมูลทั้งหมด หลังจากรีเซ็ทแล้ว อย่าลืมสร้างบัญชีผู้ใช้ใหม่แล้วล็อกอินด้วยล่ะ !
ขั้นแรก เราสร้างข้อมูลผู้ใช้ (หลอกๆ) ขึ้นมาสองสามรายการ ใส่มันเข้าไปในฐานข้อมูล แล้วนำค่า `id` ที่ได้มาสร้างอ็อบเจกต์ผู้ใช้โดยค้นหาจากฐานข้อมูลอีกที จากนั้นก็ใส่ข้อคิดเห็นเข้าไปที่ข่าวแรก โดยเชื่อมข้อคิดเห็นเข้ากับข่าว (ด้วย `postId`) และกับผู้ใช้ (ด้วย `userId`) นอกจากนี้เรายังใส่วันที่ป้อนข่าว และเนื้อหาข้อคิดเห็น ตามด้วยชื่อผู้เขียน `author` ที่เป็นฟิลด์แบบ denormalized ด้วย
อีกทั้งเรายังบอกตัวจัดการเส้นทางให้รอข้อมูล *อาร์เรย์* ที่ได้จากการบอกรับข้อมูลข้อคิดเห็นและข้อมูลข่าวด้วย
### แสดงข้อคิดเห็น
ข้อคิดเห็นก็ถูกใส่เข้าไปในฐานข้อมูลแล้ว ที่เราต้องทำต่อคือ ดึงมันออกมาแสดงในหน้าข่าวด้วยวิธีที่ตอนนี้คุณคงคุ้นเคยกันแล้ว และพอจะรู้ว่า ขั้นตอนต่อไปเราต้องทำอะไรกันบ้าง
~~~html
<template name="postPage">
<div class="post-page page">
{{> postItem}}
<ul class="comments">
{{#each comments}}
{{> commentItem}}
{{/each}}
</ul>
</div>
</template>
~~~
<%= caption "client/templates/posts/post_page.html" %>
<%= highlight "3~7" %>
~~~js
Template.postPage.helpers({
comments: function() {
return Comments.find({postId: this._id});
}
});
~~~
<%= caption "client/templates/posts/post_page.js" %>
<%= highlight "2~4" %>
เราใส่บล็อกตัวช่วย `{{#each comments}}` ลงในเทมเพลทหน้าข่าว ดังนั้น `this` ในตัวช่วย `comments` ก็คือ ข่าวที่โพสท์ การค้นหาข้อคิดเห็นของข่าวทำได้โดยดึงข้อคิดเห็นที่เชื่อมโยงกับข่าวนั้นผ่านฟิลด์ `postId`
ด้วยสิ่งที่เรารู้เกี่ยวกับตัวช่วยและ Spacebar การแสดงข้อคิดเห็นก็ทำได้ไม่ยากนัก โดยเราจะสร้างโฟลเดอร์ใหม่ชื่อ `comments` ข้างใน `templates` เพื่อไว้เก็บไฟล์เทมเพลทของข้อคิดเห็น และสร้างไฟล์เทมเพลท `commentItem` ข้างในดังนี้
~~~html
<template name="commentItem">
<li>
<h4>
<span class="author">{{author}}</span>
<span class="date">on {{submittedText}}</span>
</h4>
<p>{{body}}</p>
</li>
</template>
~~~
<%= caption "client/templates/comments/comment_item.html" %>
จากนั้นก็สร้างตัวช่วยเทมเพลทที่ใช้เปลี่ยนรูปแบบของวันที่ `submitted` ให้ดูง่ายขึ้น
~~~js
Template.commentItem.helpers({
submittedText: function() {
return this.submitted.toString();
}
});
~~~
<%= caption "client/templates/comments/comment_item.js" %>
โดยเราจะแสดงจำนวนข้อคิดเห็นของแต่ละข่าวดังนี้
~~~html
<template name="postItem">
<div class="post">
<div class="post-content">
<h3><a href="{{url}}">{{title}}</a><span>{{domain}}</span></h3>
<p>
submitted by {{author}},
<a href="{{pathFor 'postPage'}}">{{commentsCount}} comments</a>
{{#if ownPost}}<a href="{{pathFor 'postEdit'}}">Edit</a>{{/if}}
</p>
</div>
<a href="{{pathFor 'postPage'}}" class="discuss btn btn-default">Discuss</a>
</div>
</template>
~~~
<%= caption "client/templates/posts/post_item.html" %>
<%= highlight "6,7" %>
ด้วยตัวช่วย `commentsCount` ใน `post_item.js` แบบนี้
~~~js
Template.postItem.helpers({
ownPost: function() {
return this.userId === Meteor.userId();
},
domain: function() {
var a = document.createElement('a');
a.href = this.url;
return a.hostname;
},
commentsCount: function() {
return Comments.find({postId: this._id}).count();
}
});
~~~
<%= caption "client/templates/posts/post_item.js" %>
<%= highlight "9,10,11" %>
<%= commit "10-2", "Display comments on `postPage`." %>
ตอนนี้คุณก็สามารถแสดงข้อคิดเห็นจากข้อมูลที่เราใส่ไว้ได้ และเห็นหน้าจอคล้ายๆแบบนี้
<%= screenshot "10-1", "Displaying comments" %>
### ป้อนข้อคิดเห็น
ตอนนี้เราจะหาทางให้ผู้ใช้สร้างข้อคิดเห็นใหม่ได้ โดยวิธีที่เราทำจะคล้ายๆกับที่เราใช้สร้างข่าวใหม่นั่นเอง
โดยเราจะเริ่มด้วยการเพิ่มกล่องข้อคิดเห็นที่ท้ายข่าว ดังนี้
~~~html
<template name="postPage">
<div class="post-page page">
{{> postItem}}
<ul class="comments">
{{#each comments}}
{{> commentItem}}
{{/each}}
</ul>
{{#if currentUser}}
{{> commentSubmit}}
{{else}}
<p>Please log in to leave a comment.</p>
{{/if}}
</div>
</template>
~~~
<%= caption "client/templates/posts/post_page.html" %>
<%= highlight "10~14" %>
และสร้างเทมเพลทของฟอร์มกล่องข้อคิดเห็นแบบนี้
~~~html
<template name="commentSubmit">
<form name="comment" class="comment-form form">
<div class="form-group {{errorClass 'body'}}">
<div class="controls">
<label for="body">Comment on this post</label>
<textarea name="body" id="body" class="form-control" rows="3"></textarea>
<span class="help-block">{{errorMessage 'body'}}</span>
</div>
</div>
<button type="submit" class="btn btn-primary">Add Comment</button>
</form>
</template>
~~~
<%= caption "client/templates/comments/comment_submit.html" %>
ส่วนการสร้างข้อคิดเห็น เราจะเรียกใช้เมธอด `comment` จากใน `comment_submit.js` ที่ทำงานคล้ายๆกับที่เราทำตอนสร้างข่าว
~~~js
Template.commentSubmit.onCreated(function() {
Session.set('commentSubmitErrors', {});
});
Template.commentSubmit.helpers({
errorMessage: function(field) {
return Session.get('commentSubmitErrors')[field];
},
errorClass: function (field) {
return !!Session.get('commentSubmitErrors')[field] ? 'has-error' : '';
}
});
Template.commentSubmit.events({
'submit form': function(e, template) {
e.preventDefault();
var $body = $(e.target).find('[name=body]');
var comment = {
body: $body.val(),
postId: template.data._id
};
var errors = {};
if (! comment.body) {
errors.body = "Please write some content";
return Session.set('commentSubmitErrors', errors);
}
Meteor.call('commentInsert', comment, function(error, commentId) {
if (error){
throwError(error.reason);
} else {
$body.val('');
}
});
}
});
~~~
<%= caption "client/templates/comments/comment_submit.js" %>
ก็เหมือนกับที่เราทำตอนสร้างเมธอด `post` ที่รันบนฝั่งเซิร์ฟเวอร์ เราจะให้เมธอด `comment` ของ Meteor เป็นตัวสร้างข้อคิดเห็น โดยทำการตรวจสอบว่าทุกอย่างถูกต้อง แล้วจึงเพิ่มข้อคิดเห็นอันใหม่เข้าไปในคอลเลกชั่น
~~~js
Comments = new Mongo.Collection('comments');
Meteor.methods({
commentInsert: function(commentAttributes) {
check(this.userId, String);
check(commentAttributes, {
postId: String,
body: String
});
var user = Meteor.user();
var post = Posts.findOne(commentAttributes.postId);
if (!post)
throw new Meteor.Error('invalid-comment', 'You must comment on a post');
comment = _.extend(commentAttributes, {
userId: user._id,
author: user.username,
submitted: new Date()
});
return Comments.insert(comment);
}
});
~~~
<%= caption "lib/collections/comments.js" %>
<%= highlight "3~25" %>
<%= commit "10-3", "Created a form to submit comments." %>
ซึ่งเราก็ไม่ได้ทำอะไรที่มากเกินไป เราแค่ตรวจสอบว่าผู้ใช้ล็อกอินแล้ว และตรวจดูว่ามีข้อคิดเห็นมาพร้อมกับข่าวหรือไม่ เท่านั้นเอง
<%= screenshot "10-2", "The comment submit form" %>
### ควบคุมการบอกรับข้อคิดเห็น
สิ่งที่เกิดขึ้นตอนนี้ก็คือ เรากำลังเผยแพร่ข้อคิดเห็นทั้งหมดของข่าวทุกๆข่าวส่งไปยังไคลเอนต์ทุกตัวที่เชื่อมต่ออยู่ ซึ่งดูแล้วมากเกินความจำเป็น อันที่จริงแล้วในขณะใดขณะหนึ่งเราใช้แค่ข้อมูลชุดย่อยๆเท่านั้น ดังนั้นเราจะมาปรับปรุงวิธีการเผยแพร่และบอกรับข้อมูลโดยกำหนดให้ชัดเจนไปเลยว่า จะเผยแพร่ข้อคิดเห็นตัวไหนส่งออกไปบ้าง
ถ้าลองคิดดู ช่วงเวลาที่เราจำเป็นต้องบอกรับข้อมูลจากการเผยแพร่ของ `comments` จะเกิดขึ้นแค่ช่วงท่ี่ผู้ใช้งานเปิดเข้าไปดูในหน้าข่าว และเราก็แค่ต้องการโหลดข้อคิดเห็นของข่าวมาเท่านั้น
ขั้นตอนแรก เราจะทำการเปลี่ยนแปลงวิธีบอกรับข้อมูล ซึ่งที่ผ่านมาเราเขียนโค้ดไว้ที่ระดับ *ตัวจัดการเส้นทาง* หมายความว่า เราโหลดข้อมูลทั้งหมดแค่ครั้งเดียวในตอนที่ตัวจัดการเส้นทางเริ่มทำงาน
แต่ตอนนี้เราต้องการให้การบอกรับข้อมูลขึ้นอยู่กับค่าพารามิเตอร์ของเส้นทาง ซึ่งมีค่าเปลี่ยนแปลงได้ตลอดเวลา ดังนั้นเราจำเป็นต้องย้ายโค้ดบอกรับข้อมูลจากระดับของ *ตัวจัดการเส้นทาง* มาไว้ที่ระดับ *เส้นทาง* แทน
ผลกระทบที่เกิดตามมาคือ แทนที่เราจะโหลดข้อมูลตั้งแต่ตอนแอพเริ่มทำงาน เราก็จะโหลดมันเมื่อเราเปิดเข้าไปที่ *เส้นทาง* นั้นแทน หมายความว่า ในตอนนี้คุณจะต้องรอให้มีการโหลดข้อมูลทุกครั้งที่เข้าดูข้อมูลจากแอพ ซึ่งเป็นข้อเสียที่หลีกเลี่ยงไม่ได้ยกเว้นว่าคุณจะโหลดข้อมูลทั้งหมดเข้ามาตั้งแต่ตอนแรก
เราจะเริ่มจาก ยกเลิกการโหลดข้อคิดเห็นในบล็อก `configure` ด้วยการลบคำสั่ง `Meteor.subscribe('comments')` (พูดง่ายๆคือ เปลี่ยนกลับไปให้เป็นแบบเดิม)
~~~js
Router.configure({
layoutTemplate: 'layout',
loadingTemplate: 'loading',
notFoundTemplate: 'notFound',
waitOn: function() {
return Meteor.subscribe('posts');
}
});
~~~
<%= caption "lib/router.js" %>
<%= highlight "5" %>
และเพิ่มฟังก์ชัน `waitOn` ตัวใหม่ที่ระดับ *เส้นทาง* ของ `postPage`
~~~js
//...
Router.route('/posts/:_id', {
name: 'postPage',
waitOn: function() {
return Meteor.subscribe('comments', this.params._id);
},
data: function() { return Posts.findOne(this.params._id); }
});
//...
~~~
<%= caption "lib/router.js" %>
<%= highlight "5~7" %>
จะเห็นว่าเราส่งค่า `this.params._id` เป็นพารามิเตอร์อีกตัวให้กับฟังก์ชันบอกรับข้อมูล ดังนั้นเราจะนำมันมาใช้กำหนดเงื่อนไขในการดึงข้อคิดเห็นให้มาจากข่าวปัจจุบันเท่านั้น
~~~js
Meteor.publish('posts', function() {
return Posts.find();
});
Meteor.publish('comments', function(postId) {
check(postId, String);
return Comments.find({postId: postId});
});
~~~
<%= caption "server/publications.js" %>
<%= highlight "5~7" %>
<%= commit "10-4", "Made a simple publication/subscription for comments." %>
ตอนนี้เหลือแค่ปัญหาเดียว เมื่อเรากลับไปที่หน้าโฮม จะเห็นว่าข่าวทุกตัวมีจำนวนข้อคิดเห็นเป็นศูนย์
<%= screenshot "10-3", "Our comments are gone!" %>
### นับจำนวนข้อคิดเห็น
เดี๋ยวคุณก็รู้ว่าทำไมเราต้องทำ เนื่องจากเราโหลดข้อคิดเห็นเฉพาะที่เส้นทาง `postPage` เท่านั้น เมื่อเราเรียกใช้คำสั่ง `Comments.find({postId: this._id})` ในตัวช่วย `commentsCount` Meteor จึงไม่สามารถหาผลลัพธ์จากข้อมูลที่ฝั่งไคลเอนต์ให้เราได้
วิธีที่ดีที่สุดที่จะจัดการกับเรื่องนี้คือ ทำการ `denormalize` จำนวนข้อคิดเห็นไปไว้ที่ข่าว (ถ้าคุณไม่ค่อยแน่ใจว่ามันหมายถึงอะไร ไม่ต้องกังวลไป เราเตรียมเรื่องนี้ไว้ในบทแทรกตอนต่อไปแล้ว !) ซึ่งแม้ว่ามันจะทำให้โค้ดดูซับซ้อนขึ้นนิด แต่ประสิทธิภาพที่เราได้จากการที่ไม่ต้องเผยแพร่ข้อคิดเห็น _ทั้งหมด_ มาที่หน้าแสดงข่าวนั้น คุ้มค่ามากทีเดียว
โดยที่เราจะทำ คือ เพิ่มคุณสมบัติ `commentsCount` ให้กับโครงสร้างข้อมูล `post` เริ่มจากการปรับเปลี่ยนโค้ดสร้างข้อมูล (และเรียก `meteor reset` เพื่อโหลดมันเข้ามาใหม่ แล้วอย่าลืมสร้างบัญชีผู้ใช้ของคุณอีกครั้งด้วย)
~~~js
// Fixture data
if (Posts.find().count() === 0) {
var now = new Date().getTime();
// create two users
var tomId = Meteor.users.insert({
profile: { name: 'Tom Coleman' }
});
var tom = Meteor.users.findOne(tomId);
var sachaId = Meteor.users.insert({
profile: { name: 'Sacha Greif' }
});
var sacha = Meteor.users.findOne(sachaId);
var telescopeId = Posts.insert({
title: 'Introducing Telescope',
userId: sacha._id,
author: sacha.profile.name,
url: 'http://sachagreif.com/introducing-telescope/',
submitted: new Date(now - 7 * 3600 * 1000),
commentsCount: 2
});
Comments.insert({
postId: telescopeId,
userId: tom._id,
author: tom.profile.name,
submitted: new Date(now - 5 * 3600 * 1000),
body: 'Interesting project Sacha, can I get involved?'
});
Comments.insert({
postId: telescopeId,
userId: sacha._id,
author: sacha.profile.name,
submitted: new Date(now - 3 * 3600 * 1000),
body: 'You sure can Tom!'
});
Posts.insert({
title: 'Meteor',
userId: tom._id,
author: tom.profile.name,
url: 'http://meteor.com',
submitted: new Date(now - 10 * 3600 * 1000),
commentsCount: 0
});
Posts.insert({
title: 'The Meteor Book',
userId: tom._id,
author: tom.profile.name,
url: 'http://themeteorbook.com',
submitted: new Date(now - 12 * 3600 * 1000),
commentsCount: 0
});
}
~~~
<%= caption "server/fixtures.js" %>
<%= highlight "20,21,45,46,54,55" %>
ก็เหมือนอย่างเคย เมื่อคุณเปลี่ยนแปลงโค้ดสร้างข้อมูล คุณก็ต้องเรียก `meteor reset` กับฐานข้อมูลคุณ เพื่อให้แน่ใจว่ามันถูกรันอีกครั้ง
จากนั้นเราก็ทำให้แน่ใจว่า ข่าวใหม่ทุกตัวจะมีจำนวนข้อคิดเห็นเป็นศูนย์
~~~js
//...
var post = _.extend(postAttributes, {
userId: user._id,
author: user.username,
submitted: new Date(),
commentsCount: 0
});
var postId = Posts.insert(post);
//...
~~~
<%= caption "lib/collections/posts.js" %>
<%= highlight "6,7" %>
และเราก็อัพเดทค่า `commentsCount` อีกครั้งเมื่อเราสร้างข้อคิดเห็นใหม่ ด้วยการใช้ตัวดำเนินการ `$inc` ของ Mongo (ที่ใช้เพิ่มค่าฟิลด์ตัวเลขทีละหนึ่ง)
~~~js
//...
comment = _.extend(commentAttributes, {
userId: user._id,
author: user.username,
submitted: new Date()
});
// update the post with the number of comments
Posts.update(comment.postId, {$inc: {commentsCount: 1}});
return Comments.insert(comment);
//...
~~~
<%= caption "lib/collections/comments.js "%>
<%= highlight "9,10" %>
สุดท้าย เราก็แค่ลบตัวช่วย `commentsCount` ออกจาก `client/templates/posts/post_item.js` เนื่องจากเรามีฟิลด์นี้แล้วที่ตัวข่าว
<%= commit "10-5", "Denormalized the number of comments into the post." %>
ตอนนี้ผู้ใช้ก็สามารถพูดคุยกันได้ แต่มันจะน่าเสียดายแค่ไหนถ้าพวกเค้าพลาดข้อคิดเห็นใหม่ๆ และไม่ว่าคุณจะรู้อะไรมา ในบทต่อไปเราจะแสดงให้คุณเห็นวิธีใช้ การแจ้งเตือน เพื่อป้องกันเรื่องนี้ !