aboutsummaryrefslogtreecommitdiff
path: root/files/zh-tw/learn/server-side/express_nodejs/displaying_data/home_page/index.html
blob: 8adc4b11f98204d97d39a6dba533385450e4e2e0 (plain)
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
---
title: 主頁
slug: Learn/Server-side/Express_Nodejs/Displaying_data/Home_page
translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Home_page
---
<p>我們創建的第一個頁面,是網站的主頁面,可以從網站的根目錄 (<code>'/'</code>) ,或者 catalog 的根目錄 (<code>catalog/</code>) 訪問。這將呈現一些網站的靜態文字描述,以及動態計算數據庫中不同記錄類型的“計數”。</p>

<p>我們已經為主頁創建了一個路由。為了完成頁面,我們需要更新控制器函數,以從數據庫中提取記錄的“計數”,並創建一個可用於呈現頁面的視圖(模板)。</p>

<h2 id="路由">路由</h2>

<p><a href="/zh-TW/docs/Learn/Server-side/Express_Nodejs/routes">前面的教程</a>,我們創建 index 頁面路由。此處要提醒的是,所有的路由函式,都定義在<strong> /routes/catalog.js</strong>:</p>

<pre class="brush: js ">// GET catalog home page.
router.get('/', book_controller.index);  //This actually maps to /catalog/ because we import the route with a /catalog prefix</pre>

<p>Where the callback function parameter (<code>book_controller.index</code>) is defined in <strong>/controllers/bookController.js</strong>:</p>

<pre class="brush: js">exports.index = function(req, res, next) {
    res.send('NOT IMPLEMENTED: Site Home Page');
}</pre>

<p>It is this controller function that we extend to get information from our models and then render it using a template (view).</p>

<h2 id="Controller">Controller</h2>

<p>The index controller function needs to fetch information about how many <code>Book</code>, <code>BookInstance</code>, available <code>BookInstance</code>, <code>Author</code>, and <code>Genre</code> records we have in the database, render this data in a template to create an HTML page, and then return it in an HTTP response.</p>

<div class="note">
<p><strong>Note:</strong> We use the <code><a class="external external-icon" href="http://mongoosejs.com/docs/api.html#model_Model.countDocuments" rel="noopener">countDocuments()</a></code> method to get the number of instances of each model. This is called on a model with an optional set of conditions to match against in the first argument and a callback in the second argument (as discussed in <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose">Using a Database (with Mongoose)</a>, and you can also return a <code>Query</code> and then execute it with a callback later. The callback will be returned when the database returns the count, with an error value (or <code>null</code>) as the first parameter and the count of records (or null if there was an error) as the second parameter.</p>

<pre class="brush: js ">SomeModel.countDocuments({ a_model_field: 'match_value' }, function (err, count) {
 // ... do something if there is an err
 // ... do something with the count if there was no error
 });</pre>
</div>

<p>Open <strong>/controllers/bookController.js</strong>. Near the top of the file you should see the exported <code>index()</code> function.</p>

<pre class="brush: python ">var Book = require('../models/book')

exports.index = function(req, res, next) {
 res.send('NOT IMPLEMENTED: Site Home Page');
}</pre>

<p>Replace all the code above with the following code fragment. The first thing this does is import (<code>require()</code>) all the models (highlighted in bold). We need to do this because we'll be using them to get our counts of records. It then imports the <em>async</em> module.</p>

<pre class="brush: js ">var Book = require('../models/book');
var Author = require('../models/author');
var Genre = require('../models/genre');
var BookInstance = require('../models/bookinstance');

var async = require('async');

exports.index = function(req, res) {

    async.parallel({
        book_count: function(callback) {
            Book.countDocuments({}, callback); // Pass an empty object as match condition to find all documents of this collection
        },
        book_instance_count: function(callback) {
            BookInstance.countDocuments({}, callback);
        },
        book_instance_available_count: function(callback) {
            BookInstance.countDocuments({status:'Available'}, callback);
        },
        author_count: function(callback) {
            Author.countDocuments({}, callback);
        },
        genre_count: function(callback) {
            Genre.countDocuments({}, callback);
        },
    }, function(err, results) {
        res.render('index', { title: 'Local Library Home', error: err, data: results });
    });
};</pre>

<p>The <code>async.parallel()</code> method is passed an object with functions for getting the counts for each of our models. These functions are all started at the same time. When all of them have completed the final callback is invoked with the counts in the results parameter (or an error).</p>

<p>On success the callback function calls <code><a class="external external-icon" href="http://expressjs.com/en/4x/api.html#res.render" rel="noopener">res.render()</a></code>, specifying a view (template) named '<strong>index</strong>' and an object containing the data that is to be inserted into it (this includes the results object that contains our model counts). The data is supplied as key-value pairs, and can be accessed in the template using the key.</p>

<div class="note">
<p><strong>Note:</strong> The callback function from <code>async.parallel()</code> above is a little unusual in that we render the page whether or not there was an error (normally you might use a separate execution path for handling the display of errors).</p>
</div>

<h2 id="View">View</h2>

<p>Open <strong>/views/index.pug</strong> and replace its content with the text below.</p>

<pre class="brush: js ">extends layout

block content
  h1= title
  p Welcome to #[em LocalLibrary], a very basic Express website developed as a tutorial example on the Mozilla Developer Network.

  h1 Dynamic content

  if error
    p Error getting dynamic content.
  else
    p The library has the following record counts:

    ul
      li #[strong Books:] !{data.book_count}
      li #[strong Copies:] !{data.book_instance_count}
      li #[strong Copies available:] !{data.book_instance_available_count}
      li #[strong Authors:] !{data.author_count}
      li #[strong Genres:] !{data.genre_count}</pre>

<p>The view is straightforward. We extend the <strong>layout.pug</strong> base template, overriding the <code>block</code> named '<strong>content</strong>'. The first <code>h1</code> heading will be the escaped text for the <code>title</code> variable that was passed into the <code>render()</code> function—note the use of the '<code>h1=</code>' so that the following text is treated as a JavaScript expression. We then include a paragraph introducing the LocalLibrary.</p>

<p>Under the <em>Dynamic content</em> heading we check whether the error variable passed in from the <code>render()</code> function has been defined. If so, we note the error. If not, we get and list the number of copies of each model from the <code>data</code> variable.</p>

<div class="note">
<p><strong>Note:</strong> We didn't escape the count values (i.e. we used the <code>!{}</code> syntax) because the count values are calculated. If the information was supplied by end-users then we'd escape the variable for display.</p>
</div>

<h2 id="What_does_it_look_like">What does it look like?</h2>

<p>At this point we should have created everything needed to display the index page. Run the application and open your browser to <a class="external external-icon" href="http://localhost:3000/" rel="noopener">http://localhost:3000/</a>. If everything is set up correctly, your site should look something like the following screenshot.</p>

<p><img alt="Home page - Express Local Library site" src="https://mdn.mozillademos.org/files/14458/LocalLibary_Express_Home.png" style="display: block; height: 440px; margin: 0px auto; width: 1000px;"></p>

<div class="note">
<p><strong>Note:</strong> You won't be able to use the sidebar links yet because the urls, views, and templates for those pages haven't been defined. If you try you'll get errors like "NOT IMPLEMENTED: Book list" for example, depending on the link you click on.  These string literals (which will be replaced with proper data) were specified in the different controllers that live inside your "controllers" file.</p>
</div>

<h2 id="Next_steps">Next steps</h2>

<ul>
 <li>Return to <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express Tutorial Part 5: Displaying library data</a>.</li>
 <li>Proceed to the next subarticle of part 5: <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Book_list_page">Book list page</a>.</li>
</ul>