diff options
Diffstat (limited to 'files/zh-tw/learn/server-side/express_nodejs/displaying_data')
13 files changed, 1280 insertions, 0 deletions
diff --git a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/author_detail_page/index.html b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/author_detail_page/index.html new file mode 100644 index 0000000000..df7a5180e5 --- /dev/null +++ b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/author_detail_page/index.html @@ -0,0 +1,89 @@ +--- +title: 作者詳情頁面 +slug: Learn/Server-side/Express_Nodejs/Displaying_data/Author_detail_page +translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Author_detail_page +--- +<p>作者細節頁面,需要呈現指定作者 <code>Author </code>的信息,使用 <code>_id</code> 欄位的值(自動產生)識別,接著是這個作者 <code>Author </code>的所有書本物件 <code>Book </code>的列表。</p> + +<h2 id="Controller_控制器">Controller 控制器</h2> + +<p>打開 <strong>/controllers/authorController.js</strong>.</p> + +<p>在檔案最上方,加入底下幾行,引入 <em>async</em> 和 <em>Book</em> 模組(作者細節頁面需要它們)。</p> + +<pre class="brush: js">var async = require('async'); +var Book = require('../models/book');</pre> + +<p>找到 exported <code>author_detail()</code> 控制器方法,並用底下代碼置換。</p> + +<pre class="brush: js">// Display detail page for a specific Author. +exports.author_detail = function(req, res, next) { + +<strong> async.parallel({ + author: function(callback) { + Author.findById(req.params.id) + .exec(callback) + }, + authors_books: function(callback) { + Book.find({ 'author': req.params.id },'title summary') + .exec(callback) + }, + }, function(err, results) { + if (err) { return next(err); } // Error in API usage. + if (results.author==null) { // No results. + var err = new Error('Author not found'); + err.status = 404; + return next(err); + } + // Successful, so render. + res.render('author_detail', { title: 'Author Detail', author: results.author, author_books: results.authors_books } ); + });</strong> + +}; +</pre> + +<p>此處的控制器方法使用<code> async.parallel()</code>,用平行的方式,查詢作者 <code>Author</code>和相應的書本實例,並附加上繪製本頁面的回調,如果 2 個要求都成功完成,就運行回調。這個方式,就跟前面的<em>種類細節頁面</em>所說明的完全相同。</p> + +<h2 id="View_視圖">View 視圖</h2> + +<p>創建 <strong>/views/author_detail.pug</strong> ,並複制貼上底下的文字。</p> + +<pre class="brush: js">extends layout + +block content + +<strong> h1 Author: #{author.name}</strong> + p #{author.date_of_birth} - #{author.date_of_death} + + div(style='margin-left:20px;margin-top:20px') + + h4 Books + + dl + each book in author_books + dt + a(href=book.url) #{book.title} + dd #{book.summary} + + else + p This author has no books. +</pre> + +<p>本模板裡的所有事物,都在先前的章節演示過了。</p> + +<h2 id="它看起來像是">它看起來像是?</h2> + +<p>運行本應用,並打開瀏覽器訪問 <a href="http://localhost:3000/">http://localhost:3000/</a>。選擇 <em>All Authors</em> 連結,然後選擇一個作者。如果每個東西都設定正確了,你的網站看起來應該會像底下的截圖。</p> + +<p><img alt="Author Detail Page - Express Local Library site" src="https://mdn.mozillademos.org/files/14466/LocalLibary_Express_Author_Detail.png" style="border-style: solid; border-width: 1px; display: block; height: 422px; margin: 0px auto; width: 1000px;"></p> + +<div class="note"> +<p><strong>注意:</strong> 作者的出生與死亡日期的外觀很醜!我們將在本文最後的自我挑戰處理它。</p> +</div> + +<h2 id="下一步">下一步</h2> + +<ul> + <li>回到 <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express 教學 5: 呈現圖書館資料</a></li> + <li>繼續教學 5 的最後一個部分: <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_detail_page_and_challenge">書本實例詳情頁面與自我挑戰</a></li> +</ul> diff --git a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/author_list_page/index.html b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/author_list_page/index.html new file mode 100644 index 0000000000..f207126ed1 --- /dev/null +++ b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/author_list_page/index.html @@ -0,0 +1,85 @@ +--- +title: Author list page and Genre list page challenge +slug: Learn/Server-side/Express_Nodejs/Displaying_data/Author_list_page +translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Author_list_page +--- +<p>作者列表頁面,需要呈現數據庫中所有作者的列表,有每位作者的名字,並連結到作者詳細內容頁面。出生與死亡日期應該在名字後面,並且在同一列。</p> + +<h2 class="highlight-spanned" id="Controller_控制器"><span class="highlight-span">Controller </span>控制器</h2> + +<p>作者列表控制器函數,需要獲取所有作者實例的列表,然後將這些實例傳遞給模板進行渲染。</p> + +<p>打開 <strong>/controllers/authorController.js</strong>。在文件頂部附近,找到導出的 <code>author_list() </code>控制器方法,並將其替換為以下代碼(更改後的代碼以<strong>粗體</strong>顯示)。</p> + +<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="comment token">// Display list of all Authors.</span> +exports<span class="punctuation token">.</span>author_list <span class="operator token">=</span> <span class="keyword token">function</span><span class="punctuation token">(</span>req<span class="punctuation token">,</span> res<span class="punctuation token">,</span> next<span class="punctuation token">)</span> <span class="punctuation token">{</span> + + Author<span class="punctuation token">.</span><span class="function token">find</span><span class="punctuation token">(</span><span class="punctuation token">)</span> + <span class="punctuation token">.</span><span class="function token">sort</span><span class="punctuation token">(</span><span class="punctuation token">[</span><span class="punctuation token">[</span><span class="string token">'family_name'</span><span class="punctuation token">,</span> <span class="string token">'ascending'</span><span class="punctuation token">]</span><span class="punctuation token">]</span><span class="punctuation token">)</span> + <span class="punctuation token">.</span><span class="function token">exec</span><span class="punctuation token">(</span><span class="keyword token">function</span> <span class="punctuation token">(</span>err<span class="punctuation token">,</span> list_authors<span class="punctuation token">)</span> <span class="punctuation token">{</span> + <span class="keyword token">if</span> <span class="punctuation token">(</span>err<span class="punctuation token">)</span> <span class="punctuation token">{</span> <span class="keyword token">return</span> <span class="function token">next</span><span class="punctuation token">(</span>err<span class="punctuation token">)</span><span class="punctuation token">;</span> <span class="punctuation token">}</span> + <span class="comment token">//Successful, so render</span> + res<span class="punctuation token">.</span><span class="function token">render</span><span class="punctuation token">(</span><span class="string token">'author_list'</span><span class="punctuation token">,</span> <span class="punctuation token">{</span> title<span class="punctuation token">:</span> <span class="string token">'Author List'</span><span class="punctuation token">,</span> author_list<span class="punctuation token">:</span> list_authors <span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span> + <span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span> + +<span class="punctuation token">}</span><span class="punctuation token">;</span></code></pre> + +<p>The method uses the model's <code>find()</code>, <code>sort()</code> and <code>exec()</code> functions to return all <code>Author</code> objects sorted by <code>family_name</code> in alphabetic order. The callback passed to the <code>exec()</code> method is called with any errors (or <code>null</code>) as the first parameter, or a list of all authors on success. If there is an error it calls the next middleware function with the error value, and if not it renders the <strong>author_list</strong>(.pug) template, passing the page <code>title</code> and the list of authors (<code>author_list</code>).</p> + +<h2 class="highlight-spanned" id="View"><span class="highlight-span">View</span></h2> + +<p>Create <strong>/views/author_list.pug</strong> and replace its content with the text below.</p> + +<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">extends</span> <span class="class-name token">layout</span> + +block content + h1<span class="operator token">=</span> title + + ul + each author <span class="keyword token">in</span> author_list + li + <span class="function token">a</span><span class="punctuation token">(</span>href<span class="operator token">=</span>author<span class="punctuation token">.</span>url<span class="punctuation token">)</span> #<span class="punctuation token">{</span>author<span class="punctuation token">.</span>name<span class="punctuation token">}</span> + <span class="operator token">|</span> <span class="punctuation token">(</span>#<span class="punctuation token">{</span>author<span class="punctuation token">.</span>date_of_birth<span class="punctuation token">}</span> <span class="operator token">-</span> #<span class="punctuation token">{</span>author<span class="punctuation token">.</span>date_of_death<span class="punctuation token">}</span><span class="punctuation token">)</span> + + <span class="keyword token">else</span> + li There are no authors<span class="punctuation token">.</span></code></pre> + +<p>The view follows exactly the same pattern as our other templates.</p> + +<h2 class="highlight-spanned" id="What_does_it_look_like"><span class="highlight-span">What does it look like?</span></h2> + +<p>Run the application and open your browser to <a class="external external-icon" href="http://localhost:3000/" rel="noopener">http://localhost:3000/</a>. Then select the <em>All authors </em>link. If everything is set up correctly, the page should look something like the following screenshot.</p> + +<p><img alt="Author List Page - Express Local Library site" src="https://mdn.mozillademos.org/files/14468/LocalLibary_Express_Author_List.png" style="display: block; height: 453px; margin: 0px auto; width: 1200px;"></p> + +<div class="note"> +<p><strong>Note:</strong> The appearance of the author <em>lifespan </em>dates is ugly! You can improve this using the <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data#date_formatting">same approach</a> as we used for the <code>BookInstance</code> list (adding the virtual property for the lifespan to the <code>Author</code> model). This time, however, there are missing dates, and references to nonexistent properties are ignored unless strict mode is in effect. <code>moment()</code> returns the current time, and you don't want missing dates to be formatted as if they were today. One way to deal with this is to define the body of the function that returns a formatted date so it returns a blank string unless the date actually exists. For example:</p> + +<p><code>return this.date_of_birth ? moment(this.date_of_birth).format('YYYY-MM-DD') : '';</code></p> +</div> + +<h2 id="Genre_list_page—challenge!Edit">Genre list page—challenge!<a class="button section-edit only-icon" href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data$edit#Genre_list_page—challenge!" rel="nofollow, noindex"><span>Edit</span></a></h2> + +<p>In this section you should implement your own genre list page. The page should display a list of all genres in the database, with each genre linked to its associated detail page. A screenshot of the expected result is shown below.</p> + +<p><img alt="Genre List - Express Local Library site" src="https://mdn.mozillademos.org/files/14460/LocalLibary_Express_Genre_List.png" style="border-style: solid; border-width: 1px; display: block; height: 346px; margin: 0px auto; width: 600px;"></p> + +<p>The genre list controller function needs to get a list of all <code>Genre</code> instances, and then pass these to the template for rendering.</p> + +<ol> + <li>You will need to edit <code>genre_list()</code> in <strong>/controllers/genreController.js</strong>. </li> + <li>The implementation is almost exactly the same as the <code>author_list()</code> controller. + <ul> + <li>Sort the results by name, in ascending order.</li> + </ul> + </li> + <li>The template to be rendered should be named <strong>genre_list.pug</strong>.</li> + <li>The template to be rendered should be passed the variables <code>title</code> ('Genre List') and <code>genre_list</code> (the list of genres returned from your <code>Genre.find()</code> callback.</li> + <li>The view should match the screenshot/requirements above (this should have a very similar structure/format to the Author list view, except for the fact that genres do not have dates).</li> +</ol> + +<h2 id="Next_steps">Next steps</h2> + +<p>Return to <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express Tutorial Part 5: Displaying library data</a>.</p> + +<p>Proceed to the next subarticle of part 5: <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Genre_detail_page">Genre detail page</a>.</p> diff --git a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/book_detail_page/index.html b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/book_detail_page/index.html new file mode 100644 index 0000000000..31f3d65284 --- /dev/null +++ b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/book_detail_page/index.html @@ -0,0 +1,112 @@ +--- +title: 書本詳情頁面 +slug: Learn/Server-side/Express_Nodejs/Displaying_data/Book_detail_page +translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Book_detail_page +--- +<p><em>書本細節頁面</em>需要呈現一本指定書本(<code>Book</code>)的信息, 使用它的 <code>_id</code> 字段值(自動產生)做為識別,接著是圖書館中書本實例(<code>BookInstance</code>)的信息。無論我們在哪裡呈現一個作者、種類、或書本實例,都應該連結到它的細節頁面。</p> + +<h2 id="Controller_控制器">Controller 控制器</h2> + +<p>打開 <strong>/controllers/bookController.js.</strong> ,找到 exported <code>book_detail()</code> 控制器方法,用底下的代碼置換。</p> + +<pre class="brush: js">// Display detail page for a specific book. +exports.book_detail = function(req, res, next) { + +<strong> async.parallel({ + book: function(callback) { + + Book.findById(req.params.id) + .populate('author') + .populate('genre') + .exec(callback); + }, + book_instance: function(callback) { + + BookInstance.find({ 'book': req.params.id }) + .exec(callback); + }, + }, function(err, results) { + if (err) { return next(err); } + if (results.book==null) { // No results. + var err = new Error('Book not found'); + err.status = 404; + return next(err); + } + // Successful, so render. + res.render('book_detail', { title: 'Title', book: results.book, book_instances: results.book_instance } ); + });</strong> + +}; + +</pre> + +<div class="note"> +<p><strong>注意:</strong> 我們不需要用 require 導入 <em>async</em> 和 <em>BookInstance</em>,當我們實作主頁面控制器的時候,我們就已經引入這些模組。</p> +</div> + +<p>此處的控制器方法使用 <code>async.parallel()</code>,用平行的方式找到 <code>Book</code> 以及它的相應複本 (<code>BookInstances</code>) 。這樣的處理方式,就跟上面的 <em>種類細節頁面</em> 所說明的完全相同。</p> + +<h2 id="View_視圖">View 視圖</h2> + +<p>創建 <strong>/views/book_detail.pug </strong>並加入底下文字。</p> + +<pre class="brush: js">extends layout + +block content + h1 #{title}: #{book.title} + + p #[strong Author:] + a(href=book.author.url) #{book.author.name} + p #[strong Summary:] #{book.summary} + p #[strong ISBN:] #{book.isbn} + p #[strong Genre:]&nbsp; + each val, index in book.genre + a(href=val.url) #{val.name} + if index < book.genre.length - 1 + |, + + div(style='margin-left:20px;margin-top:20px') + h4 Copies + + each val in book_instances + hr + if val.status=='Available' + <strong>p.text-success</strong> #{val.status} + else if val.status=='Maintenance' + p.text-danger #{val.status} + else + p.text-warning #{val.status} + p #[strong Imprint:] #{val.imprint} + if val.status!='Available' + p #[strong Due back:] #{val.due_back} + p #[strong Id:]&nbsp; + a(href=val.url) #{val._id} + + else + p There are no copies of this book in the library. +</pre> + +<p>在這個模板裡,幾乎每個東西都在先前的章節演示過了。</p> + +<div class="note"> +<p><strong>注意:</strong> 與該書相關的種類列表,在模板中的實作,如以下代碼。除了最後一本書之外,在與本書相關的每個種類之後,都會添加一個逗號。</p> + +<pre> p #[strong Genre:] + each val, index in book.genre + a(href=val.url) #{val.name} + if index < book.genre.length - 1 + |, </pre> +</div> + +<h2 id="它看起來像是">它看起來像是?</h2> + +<p>運行本應用,並打開瀏覽器訪問 <a href="http://localhost:3000/">http://localhost:3000/</a>。選擇 <em>All books</em> 連結,然後選擇其中一本書。如果每個東西都設定正確了,你的頁面看起來應該像是底下的截圖。</p> + +<p><img alt="Book Detail Page - Express Local Library site" src="https://mdn.mozillademos.org/files/14470/LocalLibary_Express_Book_Detail.png" style="border-style: solid; border-width: 1px; display: block; height: 616px; margin: 0px auto; width: 1200px;"></p> + +<h2 id="下一步">下一步</h2> + +<ul> + <li>回到 <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express 教學 5: 呈現圖書館資料</a></li> + <li>繼續教學 5 下一個部分: <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Author_detail_page">作者詳情頁面</a></li> +</ul> diff --git a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/book_list_page/index.html b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/book_list_page/index.html new file mode 100644 index 0000000000..a35b31767d --- /dev/null +++ b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/book_list_page/index.html @@ -0,0 +1,72 @@ +--- +title: 書本清單頁面 +slug: Learn/Server-side/Express_Nodejs/Displaying_data/Book_list_page +translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Book_list_page +--- +<p>接下做我們將實作書本列表頁面。這個頁面需要呈現數據庫中所有書本的列表,包含每本書的作者、標題,標題將成為一個超連結,連到書本詳細內容頁面。</p> + +<h2 class="highlight-spanned" id="控制器">控制器</h2> + +<p>書本列表控制器函數,需要獲取數據庫中所有 <code>Book</code>對象的列表,然後將這些對像傳給模板進行呈現。</p> + +<p>打開 <strong>/controllers/bookController.js</strong>. 找到導出的 <code>book_list()</code>控制器方法,並替換為下面的代碼。</p> + +<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="comment token">// Display list of all Books.</span> +exports<span class="punctuation token">.</span>book_list <span class="operator token">=</span> <span class="keyword token">function</span><span class="punctuation token">(</span>req<span class="punctuation token">,</span> res<span class="punctuation token">,</span> next<span class="punctuation token">)</span> <span class="punctuation token">{</span> + + Book<span class="punctuation token">.</span><span class="function token">find</span><span class="punctuation token">(</span><span class="punctuation token">{</span><span class="punctuation token">}</span><span class="punctuation token">,</span> <span class="string token">'title author'</span><span class="punctuation token">)</span> + <span class="punctuation token">.</span><span class="function token">populate</span><span class="punctuation token">(</span><span class="string token">'author'</span><span class="punctuation token">)</span> + <span class="punctuation token">.</span><span class="function token">exec</span><span class="punctuation token">(</span><span class="keyword token">function</span> <span class="punctuation token">(</span>err<span class="punctuation token">,</span> list_books<span class="punctuation token">)</span> <span class="punctuation token">{</span> + <span class="keyword token">if</span> <span class="punctuation token">(</span>err<span class="punctuation token">)</span> <span class="punctuation token">{</span> <span class="keyword token">return</span> <span class="function token">next</span><span class="punctuation token">(</span>err<span class="punctuation token">)</span><span class="punctuation token">;</span> <span class="punctuation token">}</span> + <span class="comment token">//Successful, so render</span> + res<span class="punctuation token">.</span><span class="function token">render</span><span class="punctuation token">(</span><span class="string token">'book_list'</span><span class="punctuation token">,</span> <span class="punctuation token">{</span> title<span class="punctuation token">:</span> <span class="string token">'Book List'</span><span class="punctuation token">,</span> book_list<span class="punctuation token">:</span> list_books <span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span> + <span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span> + +<span class="punctuation token">}</span><span class="punctuation token">;</span></code></pre> + +<p>該方法使用模型的<code>find()</code>函數,返回所有 <code>Book </code>對象,選擇僅返回標題 <code>title </code>和作者 <code>author</code>,因為我們不需要其他字段(它也會返回 <code>_id </code>和虛擬欄位字段)。這裡我們還調用 <code>Book </code>上的 <code>populate()</code>,指定作者 <code>author</code>欄位字段 — 這將用完整的作者信息,替換儲存的書本作者 id。</p> + +<p>成功時,傳遞給查詢的回調,將呈現 <strong>book_list</strong>(.pug) 模板,將標題 <code>title </code>和<code>book_list</code>(包含作者的書本列表)作為變量傳遞。</p> + +<h2 class="highlight-spanned" id="View視圖"><span class="highlight-span">View</span>視圖</h2> + +<p>創建<strong> /views/book_list.pug</strong> 並複制底下的文字。</p> + +<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">extends</span> <span class="class-name token">layout</span> + +block content + h1<span class="operator token">=</span> title + + ul + each book <span class="keyword token">in</span> book_list + li + <span class="function token">a</span><span class="punctuation token">(</span>href<span class="operator token">=</span>book<span class="punctuation token">.</span>url<span class="punctuation token">)</span> #<span class="punctuation token">{</span>book<span class="punctuation token">.</span>title<span class="punctuation token">}</span> + <span class="operator token">|</span> <span class="punctuation token">(</span>#<span class="punctuation token">{</span>book<span class="punctuation token">.</span>author<span class="punctuation token">.</span>name<span class="punctuation token">}</span><span class="punctuation token">)</span> + + <span class="keyword token">else</span> + li There are no books<span class="punctuation token">.</span></code></pre> + +<p>這個視圖擴展了<strong> layout.pug</strong> 基本模板,並覆蓋了名為 '<strong>content</strong>' 的 <code>block </code>區塊 。它顯示我們從控制器傳入的標題 <code>title</code>(通過 <code>render()</code>方法),然後使用 <code>each</code>-<code>in</code>-<code>else </code>語法,遍歷 <code>book_list</code>變量。為每本圖書創建一個列表項,以顯示書名,並作為書的詳細信息頁面的鏈接,後面跟著作者姓名。如果 <code>book_list</code>中沒有書,則執行 <code>else</code> 子句,並顯示文字 “沒有書本” 'There are no books'。</p> + +<p> </p> + +<div class="note"> +<p><strong>注意:</strong> 我們使用 <code>book.url</code>,為每本書提供詳細記錄鏈接(我們已經實現了此路由,但尚未實現此頁面)。這是 <code>Book</code> 模型的一個虛擬屬性,它使用模型實例的 <code>_id</code> 字段,生成唯一的 URL 路徑。</p> +</div> + +<p>在這裡,我們感興趣的是,每本書被定義為兩行,第二行使用管道(上面高亮顯示)。這種方法是必要的,因為如果作者姓名位於上一行,那麼它將成為超鏈接的一部分。</p> + +<h2 class="highlight-spanned" id="它看起來像是">它看起來像是?</h2> + +<p>運行本應用 (參見 <a href="/zh-TW/docs/Learn/Server-side/Express_Nodejs/routes#Testing_the_routes">測試路由</a> 有相關的命令) ,並打開你的瀏覽器,訪問 <a class="external external-icon" href="http://localhost:3000/" rel="noopener">http://localhost:3000/</a>。然後選擇所有書本連結 <em>All books</em>。如果每樣東西都設定正確了,你的網站看起來應該像底下的截圖。</p> + +<p> </p> + +<p><img alt="Book List Page - Express Local Library site" src="https://mdn.mozillademos.org/files/14464/LocalLibary_Express_Book_List.png" style="border-style: solid; border-width: 1px; display: block; height: 387px; margin: 0px auto; width: 918px;"></p> + +<h2 id="下一步">下一步</h2> + +<ul> + <li>回到 <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express 教學 5: 呈現圖書館資料</a></li> + <li>繼續教學 5 下個部分: <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_list_page">書本實例清單頁面</a></li> +</ul> diff --git a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/bookinstance_detail_page_and_challenge/index.html b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/bookinstance_detail_page_and_challenge/index.html new file mode 100644 index 0000000000..e04981411c --- /dev/null +++ b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/bookinstance_detail_page_and_challenge/index.html @@ -0,0 +1,91 @@ +--- +title: 書本實例詳情頁面與自我挑戰 +slug: >- + Learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_detail_page_and_challenge +translation_of: >- + Learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_detail_page_and_challenge +--- +<h2 id="書本實例詳情頁面">書本實例詳情頁面</h2> + +<p><code>BookInstance</code> 詳情頁面,需要呈現每一個 <code>BookInstance </code>的信息,用 <code>_id</code> 欄位字段值(自動產生)做識別。它包含了 <code>Book</code> 名稱 (也是一個連結,連到 <em>書本細節</em>頁面),接著是紀錄中的其它的信息。</p> + +<h3 id="Controller_控制器">Controller 控制器</h3> + +<p>打開 <strong>/controllers/bookinstanceController.js</strong>. 找到 exported <code>bookinstance_detail()</code> 控制器方法,並用底下代碼置換。</p> + +<pre class="brush: js">// Display detail page for a specific BookInstance. +exports.bookinstance_detail = function(req, res, next) { + +<strong> BookInstance.findById(req.params.id) + .populate('book') + .exec(function (err, bookinstance) { + if (err) { return next(err); } + if (bookinstance==null) { // No results. + var err = new Error('Book copy not found'); + err.status = 404; + return next(err); + } + // Successful, so render. + res.render('bookinstance_detail', { title: 'Book:', bookinstance: bookinstance}); + })</strong> + +}; +</pre> + +<p>該方法使用從 URL(使用路由)中提取的特定書本實例的ID,調用<code>BookInstance.findById()</code>,並通過請求參數(<code style="font-style: normal; font-weight: normal;">req.params.id</code>),在控制器中訪問。然後調用<code>populate()</code>來獲取相關 <code>Book </code>的詳細信息。</p> + +<h3 id="View_視圖">View 視圖</h3> + +<p>創建 <strong>/views/bookinstance_detail.pug</strong> ,並複製下面的內容。</p> + +<pre class="brush: js">extends layout + +block content + +<strong> h1 ID: #{bookinstance._id}</strong> + + p #[strong Title:] + a(href=bookinstance.book.url) #{bookinstance.book.title} + p #[strong Imprint:] #{bookinstance.imprint} + + p #[strong Status:] + if bookinstance.status=='Available' + span.text-success #{bookinstance.status} + else if bookinstance.status=='Maintenance' + span.text-danger #{bookinstance.status} + else + span.text-warning #{bookinstance.status} + + if bookinstance.status!='Available' + p #[strong Due back:] #{bookinstance.due_back} +</pre> + +<p>本模組中的所有東西,都在先前的章節演示過了。</p> + +<h3 id="它看起來像是">它看起來像是?</h3> + +<p>運行本應用,並打開瀏覽器訪問 <a href="http://localhost:3000/">http://localhost:3000/</a>/。選擇 <em>All book-instances</em> 連結,然後選擇其中一本。如果每個東西都設定正確了,你的網站看起來應該像是底下的截圖。</p> + +<p><img alt="BookInstance Detail Page - Express Local Library site" src="https://mdn.mozillademos.org/files/14472/LocalLibary_Express_BookInstance_Detail.png" style="border-style: solid; border-width: 1px; display: block; height: 362px; margin: 0px auto; width: 1000px;"></p> + +<h2 id="自我挑戰">自我挑戰</h2> + +<p>目前,我們網站上顯示的大多數日期,都使用默認的 JavaScript 格式(例如 <em>Tue Dec 06 2016 15:49:58 GMT+1100 </em>(AUS東部夏令時間))。本文的挑戰,是改善作者<code>Author</code>生命週期日期顯示的外觀信息(死亡/誔生日期)和<em>BookInstance詳細信息</em>頁面,使用格式:December 6th, 2016。</p> + +<div class="note"> +<p><strong>注意:</strong> 您可以使用與我們用於 <em>Book Instance List</em> 的相同方法(將生命週期的虛擬屬性,添加到<code>Author</code>模型,並使用 <a href="https://www.npmjs.com/package/moment">moment </a>來設置日期字符串的格式)。</p> +</div> + +<p>這個挑戰的要求: </p> + +<ol> + <li>用 BookInstance 詳細信息頁面中的 <code>due_back_formatted</code> 替換 <code>due_back</code>。</li> + <li>更新作者模塊以添加壽命虛擬屬性。壽命應該有兩個值: <em>date_of_birth - date_of_death</em>,這兩個值的格式與 <code>BookInstance.due_back_formatted</code>的日期格式相同。</li> + <li>在當前使用 <code>date_of_birth</code> 和 <code>date_of_death</code>的所有視圖中,使用 <code>Author.lifespan</code> 。</li> +</ol> + +<h2 id="下一步">下一步</h2> + +<ul> + <li>回到 <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express 教學 5: 呈現圖書館資料</a></li> +</ul> diff --git a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/bookinstance_list_page/index.html b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/bookinstance_list_page/index.html new file mode 100644 index 0000000000..1b1656258e --- /dev/null +++ b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/bookinstance_list_page/index.html @@ -0,0 +1,71 @@ +--- +title: 書本實例清單頁面 +slug: Learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_list_page +translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_list_page +--- +<p>接下來,我們將實作圖書館中所有書本實例 (<code>BookInstance</code>) 的列表頁面。這個頁面需要包含與每個 <code>BookInstance</code> (鏈接到其詳細信息頁面) 關聯的書本 <code>Book</code> 標題,以及 <code>BookInstance</code>模型中的其他信息,包含每個副本的狀態,印記和唯一ID。唯一ID的文字,應該鏈接到 <code>BookInstance</code> 詳細信息頁面。</p> + +<h2 class="highlight-spanned" id="Controller_控制器"><span class="highlight-span">Controller</span> 控制器</h2> + +<p><code>BookInstance</code>列表控制器函數,需要獲取所有書本實例的列表,填充關聯的書本信息,然後將列表傳遞給模板以進行呈現。</p> + +<p>打開 <strong>/controllers/bookinstanceController.js</strong>。找到導出的 <code>bookinstance_list()</code>控制器方法,並用以下代碼替換它(更改後的代碼以<strong>粗體</strong>顯示)。</p> + +<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="comment token">// Display list of all BookInstances.</span> +exports<span class="punctuation token">.</span>bookinstance_list <span class="operator token">=</span> <span class="keyword token">function</span><span class="punctuation token">(</span>req<span class="punctuation token">,</span> res<span class="punctuation token">,</span> next<span class="punctuation token">)</span> <span class="punctuation token">{</span> + + BookInstance<span class="punctuation token">.</span><span class="function token">find</span><span class="punctuation token">(</span><span class="punctuation token">)</span> + <span class="punctuation token">.</span><span class="function token">populate</span><span class="punctuation token">(</span><span class="string token">'book'</span><span class="punctuation token">)</span> + <span class="punctuation token">.</span><span class="function token">exec</span><span class="punctuation token">(</span><span class="keyword token">function</span> <span class="punctuation token">(</span>err<span class="punctuation token">,</span> list_bookinstances<span class="punctuation token">)</span> <span class="punctuation token">{</span> + <span class="keyword token">if</span> <span class="punctuation token">(</span>err<span class="punctuation token">)</span> <span class="punctuation token">{</span> <span class="keyword token">return</span> <span class="function token">next</span><span class="punctuation token">(</span>err<span class="punctuation token">)</span><span class="punctuation token">;</span> <span class="punctuation token">}</span> + <span class="comment token">// Successful, so render</span> + res<span class="punctuation token">.</span><span class="function token">render</span><span class="punctuation token">(</span><span class="string token">'bookinstance_list'</span><span class="punctuation token">,</span> <span class="punctuation token">{</span> title<span class="punctuation token">:</span> <span class="string token">'Book Instance List'</span><span class="punctuation token">,</span> bookinstance_list<span class="punctuation token">:</span> list_bookinstances <span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span> + <span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span> + +<span class="punctuation token">}</span><span class="punctuation token">;</span></code></pre> + +<p>此方法使用模型的<code>find()</code>函數,返回所有 <code>BookInstance</code>對象。然後它將一個調用,以菊花鏈方式連接到 <code>populate()</code>,附加書本 <code>book</code>欄位字段,這將使用完整的 <code>Book</code>文檔,替換每個 <code>BookInstance</code>儲存的書本 ID。</p> + +<p>成功時,傳遞給查詢的回調,會呈現 <strong>bookinstance_list</strong> (.pug)模板,並將標題<code>title</code>和書籍實例列表 <code>bookinstance_list</code>作為變量傳遞。</p> + +<h2 class="highlight-spanned" id="View_視圖"><span class="highlight-span">View</span> 視圖</h2> + +<p>創建 <strong>/views/bookinstance_list.pug </strong>,並複制貼上底下的文字。</p> + +<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">extends</span> <span class="class-name token">layout</span> + +block content + h1<span class="operator token">=</span> title + + ul + each val <span class="keyword token">in</span> bookinstance_list + li + <span class="function token">a</span><span class="punctuation token">(</span>href<span class="operator token">=</span>val<span class="punctuation token">.</span>url<span class="punctuation token">)</span> #<span class="punctuation token">{</span>val<span class="punctuation token">.</span>book<span class="punctuation token">.</span>title<span class="punctuation token">}</span> <span class="punctuation token">:</span> #<span class="punctuation token">{</span>val<span class="punctuation token">.</span>imprint<span class="punctuation token">}</span> <span class="operator token">-</span> + <span class="keyword token">if</span> val<span class="punctuation token">.</span>status<span class="operator token">==</span><span class="string token">'Available'</span> + span<span class="punctuation token">.</span>text<span class="operator token">-</span>success #<span class="punctuation token">{</span>val<span class="punctuation token">.</span>status<span class="punctuation token">}</span> + <span class="keyword token">else</span> <span class="keyword token">if</span> val<span class="punctuation token">.</span>status<span class="operator token">==</span><span class="string token">'Maintenance'</span> + span<span class="punctuation token">.</span>text<span class="operator token">-</span>danger #<span class="punctuation token">{</span>val<span class="punctuation token">.</span>status<span class="punctuation token">}</span> + <span class="keyword token">else</span> + span<span class="punctuation token">.</span>text<span class="operator token">-</span>warning #<span class="punctuation token">{</span>val<span class="punctuation token">.</span>status<span class="punctuation token">}</span> + <span class="keyword token">if</span> val<span class="punctuation token">.</span>status<span class="operator token">!=</span><span class="string token">'Available'</span> + span <span class="function token"> </span><span class="punctuation token">(</span>Due<span class="punctuation token">:</span> #<span class="punctuation token">{</span>val<span class="punctuation token">.</span>due_back<span class="punctuation token">}</span> <span class="punctuation token">)</span> + + <span class="keyword token">else</span> + li There are no book copies <span class="keyword token">in</span> <span class="keyword token">this</span> library<span class="punctuation token">.</span></code></pre> + +<p>這個視圖與其他視圖非常相似。它擴展了佈局,替換內容區塊,顯示從控制器傳入的標題 <code>title</code>,並遍歷 <code>bookinstance_list</code> 中的所有書籍副本。對於每個副本,我們都會顯示它的狀態(用顏色編碼),如果書本不可用,則顯示其預期返回日期。這裡引入了一個新功能 — 我們可以在標籤之後使用點符號表示法,來指定一個類別。因此,<code>span.text-success </code>將被編譯為 <<code>span class="text-success"</code>> (也可以用 Pug 編寫為 <code>span(class="text-success"</code>)。</p> + +<p> </p> + +<h2 class="highlight-spanned" id="它看起來像是">它看起來像是?</h2> + +<p>運行本應用,打開瀏覽器訪問 <a class="external external-icon" href="http://localhost:3000/" rel="noopener">http://localhost:3000/</a>,然後選擇 All book-instances 連結。假如每個東西都設定正確了,你的網站看起來應該像是底下的截圖。</p> + +<p><img alt="BookInstance List Page - Express Local Library site" src="https://mdn.mozillademos.org/files/14474/LocalLibary_Express_BookInstance_List.png" style="border-style: solid; border-width: 1px; display: block; height: 322px; margin: 0px auto; width: 1200px;"></p> + +<h2 id="下一步">下一步</h2> + +<ul> + <li>回到 <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express 教學 5: 呈現圖書館資料</a></li> + <li>繼續教學 5下一個部分: <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Date_formatting_using_moment">格式化日期 - 使用 moment</a>.</li> +</ul> diff --git a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/date_formatting_using_moment/index.html b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/date_formatting_using_moment/index.html new file mode 100644 index 0000000000..ecd3ee7f0d --- /dev/null +++ b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/date_formatting_using_moment/index.html @@ -0,0 +1,60 @@ +--- +title: 使用 moment 做日期格式化 +slug: Learn/Server-side/Express_Nodejs/Displaying_data/Date_formatting_using_moment +translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Date_formatting_using_moment +--- +<p>我們模型的日期預設呈現很難看: <em>Tue Dec 06 2016 15:49:58 GMT+1100 (AUS Eastern Daylight Time)</em>。在本節中,我們將展示如何更新上一節中的 <em>書本實例 BookInstance 列表頁面</em>,以更友好的格式顯示<code>due_date</code>欄位字段:December 6th, 2016。</p> + +<p>我們將使用的方法,是在我們的<code>BookInstance</code>模型中,創建一個返回格式化日期的虛擬屬性。我們將使用 <a class="external external-icon" href="https://www.npmjs.com/package/moment" rel="noopener">moment</a> 來做實際的格式化,這是一個輕量級 JavaScript日期庫,用於解析,驗證,操作和格式化日期。</p> + +<div class="note"> +<p><strong>注意:</strong> 我們可以直接在 Pug 模板中,使用 moment 格式化字符串,或者可以在許多其它地方格式化字符串。使用虛擬屬性,可以使我們獲得格式化的日期,這與我們當前獲取 <code>due_date</code> 的方式完全相同。</p> +</div> + +<h2 class="highlight-spanned" id="安裝_moment"><span class="highlight-span">安裝 moment</span></h2> + +<p>在項目的根目錄,輸入下列命令</p> + +<p> </p> + +<pre class="brush: bash line-numbers language-bash"><code class="language-bash">npm install moment</code></pre> + +<h2 class="highlight-spanned" id="創建虛擬屬性">創建虛擬屬性</h2> + +<ol> + <li>打開 <strong>./models/bookinstance.js</strong>.</li> + <li>在此頁面最上方,引入 <em>moment</em>. + <pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">var</span> moment <span class="operator token">=</span> <span class="function token">require</span><span class="punctuation token">(</span><span class="string token">'moment'</span><span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre> + </li> +</ol> + +<p>在 url 屬性後面,加入虛擬屬性 <code>due_back_formatted</code> 。</p> + +<pre class="brush: js line-numbers language-js"><code class="language-js">BookInstanceSchema +<span class="punctuation token">.</span><span class="function token">virtual</span><span class="punctuation token">(</span><span class="string token">'due_back_formatted'</span><span class="punctuation token">)</span> +<span class="punctuation token">.</span><span class="keyword token">get</span><span class="punctuation token">(</span><span class="keyword token">function</span> <span class="punctuation token">(</span><span class="punctuation token">)</span> <span class="punctuation token">{</span> + <span class="keyword token">return</span> <span class="function token">moment</span><span class="punctuation token">(</span><span class="keyword token">this</span><span class="punctuation token">.</span>due_back<span class="punctuation token">)</span><span class="punctuation token">.</span><span class="function token">format</span><span class="punctuation token">(</span><span class="string token">'MMMM Do, YYYY'</span><span class="punctuation token">)</span><span class="punctuation token">;</span> +<span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre> + +<div class="note"> +<p><strong>注意:</strong> 格式化方法可以使用幾乎任何模式顯示日期。 <a class="external external-icon" href="http://momentjs.com/docs/#/displaying/" rel="noopener">moment</a>文檔中,可以找到表示不同日期組件的語法。</p> +</div> + +<h2 class="highlight-spanned" id="更新視圖">更新視圖</h2> + +<p>打開 <strong>/views/bookinstance_list.pug</strong> ,然後用 <code>due_back_formatted</code> 取代 <code>due_back</code> 。</p> + +<pre class="brush: js line-numbers language-js"><code class="language-js"> <span class="keyword token">if</span> val<span class="punctuation token">.</span>status<span class="operator token">!=</span><span class="string token">'Available'</span> + <span class="comment token">//span (Due: #{val.due_back} )</span> + span <span class="function token"> </span><span class="punctuation token">(</span>Due<span class="punctuation token">:</span> #<span class="punctuation token">{</span>val<span class="punctuation token">.</span>due_back_formatted<span class="punctuation token">}</span> <span class="punctuation token">)</span> </code></pre> + +<p>這就是本章節的全部了。如果你訪問側邊欄的 <em>All book-instances</em> ,你應該會看到所有的歸還日期都更吸引人了!</p> + +<h2 id="下一步">下一步</h2> + +<ul> + <li>回到 <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express 教學 5: 呈現圖書館資料</a></li> + <li>繼續教學 5 的下一個部分: <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Author_list_page">Author list page and Genre list page challenge</a>.</li> +</ul> + +<p> </p> diff --git a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/flow_control_using_async/index.html b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/flow_control_using_async/index.html new file mode 100644 index 0000000000..5271bd6722 --- /dev/null +++ b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/flow_control_using_async/index.html @@ -0,0 +1,137 @@ +--- +title: 使用 async 進行非同步流控制 +slug: Learn/Server-side/Express_Nodejs/Displaying_data/flow_control_using_async +translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/flow_control_using_async +--- +<p>有些<em>本地圖書館</em>網頁的控制器代碼,會依賴多重非同步要求的結果,可能會需要以某種特定次序運行,或者以平行方式運行。為了管理流控制,並在我們所有需要用到的信息,都已經可以取用的時候,再繪製網頁,我們將使用許多人採用的 node <a class="external external-icon" href="https://www.npmjs.com/package/async" rel="noopener">async</a> 模組。</p> + +<div class="note"> +<p><strong>注意:</strong> 在 JavaScript 中有許多其他方法,可以管理異步行為和流控制,包括相對較新的 JavaScript 語言功能,如 <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Techniques/Promises">Promises</a>。</p> +</div> + +<p>Async 有很多有用的方法(請查看<a href="http://caolan.github.io/async/docs.html">文檔</a>)。一些最重要的功能是:</p> + +<p> </p> + +<ul> + <li><code><a class="external external-icon" href="http://caolan.github.io/async/docs.html#parallel" rel="noopener">async.parallel()</a></code> 執行必須並行執行的任何操作。</li> + <li><code><a class="external external-icon" href="http://caolan.github.io/async/docs.html#series" rel="noopener">async.series()</a></code> 用於當需要確保異步操作是序列執行的。</li> + <li><code><a class="external external-icon" href="http://caolan.github.io/async/docs.html#waterfall" rel="noopener">async.waterfall()</a></code> 用於必須序列運行的操作,每個操作取決於前面操作的結果。</li> +</ul> + +<h2 class="highlight-spanned" id="為什麼需要這麼做">為什麼需要這麼做?</h2> + +<p>我們在 <em>Express</em> 中使用的大多數方法,都是異步的 - 您指定要執行的操作,傳遞回調。該方法立即返回,並在請求的操作完成時,調用回調。按照 <em>Express</em> 中的慣例,回調函數將<em>錯誤值</em>作為第一個參數傳遞(或成功時為 <code>null</code>),並將函數的結果(如果有的話)作為第二個參數傳遞。</p> + +<p>如果控制器只需要<em>執行<strong>一個</strong>異步操作</em>,來獲取呈現頁面所需的信息,那麼實現很簡單 - 我們只需在回調中呈現模板。下面的代碼片段,顯示了一個函數,該函數呈現模型 <code>SomeModel</code> 的計數(使用Mongoose <code><a class="external external-icon" href="http://mongoosejs.com/docs/api.html#model_Model.count" rel="noopener">count()</a></code>方法):</p> + +<pre class="brush: js"><code>exports.some_model_count = function(req, res, next) { + +</code> SomeModel.count({ a_model_field: 'match_value' }, function (err, count) { + // ... do something if there is an err + + // On success, render the result by passing count into the render function (here, as the variable 'data'). + res.render('the_template', { data: count } ); + }); +<code>}</code> +</pre> + +<p>但是,如果您需要進行<strong>多個</strong>異步查詢,並且在完成所有操作之前,無法呈現頁面,該怎麼辦?一個單純的實現可以用 “菊花鏈” 連接請求,在先前請求的回調中,啟動後續請求,並在最終回調中呈現響應。這種方法的問題,是我們的請求必須串行運行,即使並行運行它們可能更有效。這也可能導致複雜的嵌套代碼,通常稱為<a href="http://callbackhell.com/">回調地獄</a>。</p> + +<p>一個更好的解決方案,是並行執行所有請求,然後在所有查詢完成後執行單個回調。這是 <em>Async</em> 模塊簡化的流操作!</p> + +<h2 class="highlight-spanned" id="Asynchronous_operations_in_parallel"><span class="highlight-span">Asynchronous operations in parallel</span></h2> + +<p>The method <code><a class="external external-icon" href="http://caolan.github.io/async/docs.html#parallel" rel="noopener">async.parallel()</a></code> is used to run multiple asynchronous operations in parallel.</p> + +<p>The first argument to <code>async.parallel()</code> is a collection of the asynchronous functions to run (an array, object or other iterable). Each function is passed a <code>callback(err, result)</code> which it must call on completion with an error <code>err</code> (which can be <code>null</code>) and an optional <code>results</code> value.</p> + +<p>The optional second argument to <code>async.parallel()</code> is a callback that will be run when all the functions in the first argument have completed. The callback is invoked with an error argument and a result collection that contains the results of the individual asynchronous operations. The result collection is of the same type as the first argument (i.e. if you pass an array of asynchronous functions, the final callback will be invoked with an array of results). If any of the parallel functions reports an error the callback is invoked early (with the error value).</p> + +<p>The example below shows how this works when we pass an object as the first argument. As you can see, the results are <em>returned</em> in an object with the same property names as the original functions that were passed in.</p> + +<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">async</span><span class="punctuation token">.</span><span class="function token">parallel</span><span class="punctuation token">(</span><span class="punctuation token">{</span> + one<span class="punctuation token">:</span> <span class="keyword token">function</span><span class="punctuation token">(</span>callback<span class="punctuation token">)</span> <span class="punctuation token">{</span> <span class="punctuation token">.</span><span class="punctuation token">.</span><span class="punctuation token">.</span> <span class="punctuation token">}</span><span class="punctuation token">,</span> + two<span class="punctuation token">:</span> <span class="keyword token">function</span><span class="punctuation token">(</span>callback<span class="punctuation token">)</span> <span class="punctuation token">{</span> <span class="punctuation token">.</span><span class="punctuation token">.</span><span class="punctuation token">.</span> <span class="punctuation token">}</span><span class="punctuation token">,</span> + <span class="punctuation token">.</span><span class="punctuation token">.</span><span class="punctuation token">.</span> + something_else<span class="punctuation token">:</span> <span class="keyword token">function</span><span class="punctuation token">(</span>callback<span class="punctuation token">)</span> <span class="punctuation token">{</span> <span class="punctuation token">.</span><span class="punctuation token">.</span><span class="punctuation token">.</span> <span class="punctuation token">}</span> + <span class="punctuation token">}</span><span class="punctuation token">,</span> + <span class="comment token">// optional callback</span> + <span class="keyword token">function</span><span class="punctuation token">(</span>err<span class="punctuation token">,</span> results<span class="punctuation token">)</span> <span class="punctuation token">{</span> + <span class="comment token">// 'results' is now equal to: {one: 1, two: 2, ..., something_else: some_value}</span> + <span class="punctuation token">}</span> +<span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre> + +<p>If you instead pass an array of functions as the first argument, the results will be an array (the array order results will match the original order that the functions were declared—not the order in which they completed).</p> + +<h2 class="highlight-spanned" id="Asynchronous_operations_in_series"><span class="highlight-span">Asynchronous operations in series</span></h2> + +<p>The method <code><a class="external external-icon" href="http://caolan.github.io/async/docs.html#series" rel="noopener">async.series()</a></code> is used to run multiple asynchronous operations in sequence, when subsequent functions do not depend on the output of earlier functions. It is essentially declared and behaves in the same way as <code>async.parallel()</code>.</p> + +<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">async</span><span class="punctuation token">.</span><span class="function token">series</span><span class="punctuation token">(</span><span class="punctuation token">{</span> + one<span class="punctuation token">:</span> <span class="keyword token">function</span><span class="punctuation token">(</span>callback<span class="punctuation token">)</span> <span class="punctuation token">{</span> <span class="punctuation token">.</span><span class="punctuation token">.</span><span class="punctuation token">.</span> <span class="punctuation token">}</span><span class="punctuation token">,</span> + two<span class="punctuation token">:</span> <span class="keyword token">function</span><span class="punctuation token">(</span>callback<span class="punctuation token">)</span> <span class="punctuation token">{</span> <span class="punctuation token">.</span><span class="punctuation token">.</span><span class="punctuation token">.</span> <span class="punctuation token">}</span><span class="punctuation token">,</span> + <span class="punctuation token">.</span><span class="punctuation token">.</span><span class="punctuation token">.</span> + something_else<span class="punctuation token">:</span> <span class="keyword token">function</span><span class="punctuation token">(</span>callback<span class="punctuation token">)</span> <span class="punctuation token">{</span> <span class="punctuation token">.</span><span class="punctuation token">.</span><span class="punctuation token">.</span> <span class="punctuation token">}</span> + <span class="punctuation token">}</span><span class="punctuation token">,</span> + <span class="comment token">// optional callback after the last asynchronous function completes.</span> + <span class="keyword token">function</span><span class="punctuation token">(</span>err<span class="punctuation token">,</span> results<span class="punctuation token">)</span> <span class="punctuation token">{</span> + <span class="comment token">// 'results' is now equals to: {one: 1, two: 2, ..., something_else: some_value} </span> + <span class="punctuation token">}</span> +<span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre> + +<div class="note"> +<p><strong>Note:</strong> The ECMAScript (JavaScript) language specification states that the order of enumeration of an object is undefined, so it is possible that the functions will not be called in the same order as you specify them on all platforms. If the order really is important, then you should pass an array instead of an object, as shown below.</p> +</div> + +<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">async</span><span class="punctuation token">.</span><span class="function token">series</span><span class="punctuation token">(</span><span class="punctuation token">[</span> + <span class="keyword token">function</span><span class="punctuation token">(</span>callback<span class="punctuation token">)</span> <span class="punctuation token">{</span> + <span class="comment token">// do some stuff ...</span> + <span class="function token">callback</span><span class="punctuation token">(</span><span class="keyword token">null</span><span class="punctuation token">,</span> <span class="string token">'one'</span><span class="punctuation token">)</span><span class="punctuation token">;</span> + <span class="punctuation token">}</span><span class="punctuation token">,</span> + <span class="keyword token">function</span><span class="punctuation token">(</span>callback<span class="punctuation token">)</span> <span class="punctuation token">{</span> + <span class="comment token">// do some more stuff ... </span> + <span class="function token">callback</span><span class="punctuation token">(</span><span class="keyword token">null</span><span class="punctuation token">,</span> <span class="string token">'two'</span><span class="punctuation token">)</span><span class="punctuation token">;</span> + <span class="punctuation token">}</span> + <span class="punctuation token">]</span><span class="punctuation token">,</span> + <span class="comment token">// optional callback</span> + <span class="keyword token">function</span><span class="punctuation token">(</span>err<span class="punctuation token">,</span> results<span class="punctuation token">)</span> <span class="punctuation token">{</span> + <span class="comment token">// results is now equal to ['one', 'two'] </span> + <span class="punctuation token">}</span> +<span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre> + +<h2 class="highlight-spanned" id="Dependent_asynchronous_operations_in_series"><span class="highlight-span">Dependent asynchronous operations in series</span></h2> + +<p>The method <code><a class="external external-icon" href="http://caolan.github.io/async/docs.html#waterfall" rel="noopener">async.waterfall()</a></code> is used to run multiple asynchronous operations in sequence when each operation is dependent on the result of the previous operation.</p> + +<p>The callback invoked by each asynchronous function contains <code>null</code> for the first argument and results in subsequent arguments. Each function in the series takes the results arguments of the previous callback as the first parameters, and then a callback function. When all operations are complete, a final callback is invoked with the result of the last operation. The way this works is more clear when you consider the code fragment below (this example is from the <em>async</em> documentation):</p> + +<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="keyword token">async</span><span class="punctuation token">.</span><span class="function token">waterfall</span><span class="punctuation token">(</span><span class="punctuation token">[</span> + <span class="keyword token">function</span><span class="punctuation token">(</span>callback<span class="punctuation token">)</span> <span class="punctuation token">{</span> + <span class="function token">callback</span><span class="punctuation token">(</span><span class="keyword token">null</span><span class="punctuation token">,</span> <span class="string token">'one'</span><span class="punctuation token">,</span> <span class="string token">'two'</span><span class="punctuation token">)</span><span class="punctuation token">;</span> + <span class="punctuation token">}</span><span class="punctuation token">,</span> + <span class="keyword token">function</span><span class="punctuation token">(</span>arg1<span class="punctuation token">,</span> arg2<span class="punctuation token">,</span> callback<span class="punctuation token">)</span> <span class="punctuation token">{</span> + <span class="comment token">// arg1 now equals 'one' and arg2 now equals 'two' </span> + <span class="function token">callback</span><span class="punctuation token">(</span><span class="keyword token">null</span><span class="punctuation token">,</span> <span class="string token">'three'</span><span class="punctuation token">)</span><span class="punctuation token">;</span> + <span class="punctuation token">}</span><span class="punctuation token">,</span> + <span class="keyword token">function</span><span class="punctuation token">(</span>arg1<span class="punctuation token">,</span> callback<span class="punctuation token">)</span> <span class="punctuation token">{</span> + <span class="comment token">// arg1 now equals 'three'</span> + <span class="function token">callback</span><span class="punctuation token">(</span><span class="keyword token">null</span><span class="punctuation token">,</span> <span class="string token">'done'</span><span class="punctuation token">)</span><span class="punctuation token">;</span> + <span class="punctuation token">}</span> +<span class="punctuation token">]</span><span class="punctuation token">,</span> <span class="keyword token">function</span> <span class="punctuation token">(</span>err<span class="punctuation token">,</span> result<span class="punctuation token">)</span> <span class="punctuation token">{</span> + <span class="comment token">// result now equals 'done'</span> +<span class="punctuation token">}</span> +<span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre> + +<h2 class="highlight-spanned" id="Installing_async"><span class="highlight-span">Installing async</span></h2> + +<p>Install the async module using the NPM package manager so that we can use it in our code. You do this in the usual way, by opening a prompt in the root of the <em>LocalLibrary</em> project and enter the following command:</p> + +<pre class="brush: bash line-numbers language-bash"><code class="language-bash">npm install async</code></pre> + +<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/Template_primer">Template primer</a>.</li> +</ul> diff --git a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/genre_detail_page/index.html b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/genre_detail_page/index.html new file mode 100644 index 0000000000..2c7f1e938b --- /dev/null +++ b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/genre_detail_page/index.html @@ -0,0 +1,123 @@ +--- +title: Genre detail page +slug: Learn/Server-side/Express_Nodejs/Displaying_data/Genre_detail_page +translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Genre_detail_page +--- +<p>種類<em>細節</em>頁面,需要利用<code>_id</code> 字段值 (自動生成) ,以呈現特定種類實例的信息。此頁面應該呈現種類名稱,各個種類的所有書本列表(每本書都連結到書本的細節頁面)。</p> + +<p> </p> + +<h2 id="Controller_控制器">Controller 控制器</h2> + +<p>打開 <strong>/controllers/genreController.js</strong> ,並在檔案最上方引用 <em>async</em> 和 <em>Book</em> 模組。</p> + +<pre class="brush: js">var Book = require('../models/book'); +var async = require('async'); +</pre> + +<p>Find the exported <code>genre_detail</code><code>()</code> controller method and replace it with the following code.</p> + +<pre class="brush: js">// Display detail page for a specific Genre. +exports.genre_detail = function(req, res, next) { + +<strong> async.parallel({ + genre: function(callback) { + Genre.findById(req.params.id) + .exec(callback); + }, + + genre_books: function(callback) { + Book.find({ 'genre': req.params.id }) + .exec(callback); + }, + + }, function(err, results) { + if (err) { return next(err); } + if (results.genre==null) { // No results. + var err = new Error('Genre not found'); + err.status = 404; + return next(err); + } + // Successful, so render + res.render('genre_detail', { title: 'Genre Detail', genre: results.genre, genre_books: results.genre_books } ); + });</strong> + +}; +</pre> + +<p>The method uses <code>async.parallel()</code> to query the genre name and its associated books in parallel, with the callback rendering the page when (if) both requests complete successfully.</p> + +<p>The ID of the required genre record is encoded at the end of the URL and extracted automatically based on the route definition (<strong>/genre/:id</strong>). The ID is accessed within the controller via the request parameters: <code style="font-style: normal; font-weight: normal;">req.params.id</code>. It is used in <code style="font-style: normal; font-weight: normal;">Genre.findById()</code> to get the current genre. It is also used to get all <code>Book</code> objects that have the genre ID in their <code>genre</code> field: <code>Book.find({ 'genre': req.params.id })</code>.</p> + +<div class="note"> +<p><strong>Note:</strong> If the genre does not exist in the database (i.e. it may have been deleted) then <code>findById()</code> will return successfully with no results. In this case we want to display a "not found" page, so we create an <code>Error</code> object and pass it to the <code>next</code> middleware function in the chain. </p> + +<pre class="brush: js"><strong>if (results.genre==null) { // No results. + var err = new Error('Genre not found'); + err.status = 404; + return next(err); +}</strong> +</pre> + +<p>The message will then propagate through to our error handling code (this was set up when we <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website#error_handling">generated the app skeleton</a> - for more information see <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction#Handling_errors">Handling Errors</a>).</p> +</div> + +<p>The rendered view is <strong>genre_detail</strong> and it is passed variables for the <code>title</code>, <code>genre</code> and the list of books in this genre (<code>genre_books</code>).</p> + +<h2 id="View">View</h2> + +<p>Create <strong>/views/genre_detail.pug</strong> and fill it with the text below:</p> + +<pre class="brush: js">extends layout + +block content + + <strong>h1 Genre: #{genre.name}</strong> + + div(style='margin-left:20px;margin-top:20px') + + h4 Books + + dl + each book in genre_books + dt + a(href=book.url) #{book.title} + dd #{book.summary} + + else + p This genre has no books +</pre> + +<p>The view is very similar to all our other templates. The main difference is that we don't use the <code>title</code> passed in for the first heading (though it is used in the underlying <strong>layout.pug</strong> template to set the page title).</p> + +<h2 id="What_does_it_look_like">What does it look like?</h2> + +<p>Run the application and open your browser to <a href="http://localhost:3000/">http://localhost:3000/</a>. Select the <em>All genres</em> link, then select one of the genres (e.g. "Fantasy"). If everything is set up correctly, your page should look something like the following screenshot.</p> + +<p><img alt="Genre Detail Page - Express Local Library site" src="https://mdn.mozillademos.org/files/14462/LocalLibary_Express_Genre_Detail.png" style="border-style: solid; border-width: 1px; display: block; height: 523px; margin: 0px auto; width: 1000px;"></p> + +<div class="note"> +<p>You might get an error similar to this:</p> + +<pre class="brush: bash">Cast to ObjectId failed for value " 59347139895ea23f9430ecbb" at path "_id" for model "Genre" +</pre> + +<p>This is a mongoose error coming from the <strong>req.params.id</strong>. To solve this problem, first you need to require mongoose on the <strong>genreController.js</strong> page like this:</p> + +<pre class="brush: js"> var mongoose = require('mongoose'); +</pre> + +<p>Then use <strong>mongoose.Types.ObjectId() </strong>to convert the id to a that can be used. For example:</p> + +<pre class="brush: js">exports.genre_detail = function(req, res, next) { + var id = mongoose.Types.ObjectId(req.params.id); + ... +</pre> +</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_detail_page">Book detail page</a>.</li> +</ul> diff --git a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/home_page/index.html b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/home_page/index.html new file mode 100644 index 0000000000..8adc4b11f9 --- /dev/null +++ b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/home_page/index.html @@ -0,0 +1,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> diff --git a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/index.html b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/index.html new file mode 100644 index 0000000000..2073a02bc8 --- /dev/null +++ b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/index.html @@ -0,0 +1,87 @@ +--- +title: 'Express 教程 5: 呈現圖書館數據' +slug: Learn/Server-side/Express_Nodejs/Displaying_data +translation_of: Learn/Server-side/Express_Nodejs/Displaying_data +--- +<div>{{LearnSidebar}}</div> + +<div>{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/routes", "Learn/Server-side/Express_Nodejs/forms", "Learn/Server-side/Express_Nodejs")}}</div> + +<p class="summary">我們現在準備好要新增網頁,以顯示本地圖書館網站的書本與其它資料。這些網頁將包括一個主頁 ,顯示我們每個模型的型態有多少筆紀錄,以及我們所有模型的清單與細節頁面。藉此,我們將得到從數據庫取得紀錄、以及使用樣版的實務經驗。</p> + +<table class="learn-box standard-table"> + <tbody> + <tr> + <th scope="row">前置條件:</th> + <td>完成先前教程主題 (包含 Express 教程 4: 路由與控制器)。</td> + </tr> + <tr> + <th scope="row">目標:</th> + <td>了解如何使用非同步模組與 Pug 樣版語言,以及如何從我們的控制器函式中的 URL 得取資料。</td> + </tr> + </tbody> +</table> + +<h2 id="概覽">概覽</h2> + +<p>在我們先前的教程中,定義了可以用來跟資料庫互動的 <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose">Mongoose models</a> ,並創建了一些初始的圖書館紀錄。我們接著<a href="/zh-TW/docs/Learn/Server-side/Express_Nodejs/routes">創建本地圖書館網站需要的所有路由</a>,但僅使用"空殼控制器" 函式(這些是骨架控制器函式,當一個網頁被存取時,只回傳一個"未實作" 信息)。</p> + +<p>下一步,是為這些顯示圖書館信息的網頁,提供充分的實作(我們將在後面的文章,檢視網頁表單的實作,像是創建、更新、刪除信息)。這包含了更新控制器函式,以利用我們的模型取得紀錄,並定義模板,為使用者顯示這些信息。</p> + +<p>我們在一開始,提供概略的總覽/重點主題,解釋在控制器函式中,如何管理非同步操作,以及如何使用 Pug 撰寫模板。然後我們將為每一個主要的 "唯讀" 網頁提供實作步驟,並且在使用到任何特別的、或新的特性時,會附上簡短的解釋說明。</p> + +<p>本教程的最後,你對路由、非同步函式、視圖、模型如何實際運作,應該有了更好的理解。</p> + +<h2 id="本教程的章節">本教程的章節</h2> + +<p>本教程分為下列章節,說明為了顯示圖書館網站頁面,如何新增各種特性 。在進入下一個教程之前,你需要閱讀並逐一實作下列章節。</p> + +<ol> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/flow_control_using_async">使用 async 控制非同步流</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Template_primer">模板入門</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/LocalLibrary_base_template">本地圖書館基礎模板</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Home_page">主頁</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Book_list_page">書本清單頁面</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_list_page">書本實例清單頁面</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Date_formatting_using_moment">日期格式化 - 使用 moment</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Author_list_page">作者清單頁面、分類清單頁面、與自我挑戰</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Genre_detail_page">分類詳情頁面</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Book_detail_page">書本詳情頁面</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Author_detail_page">作者詳情頁面</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_detail_page_and_challenge">書本實例詳情頁面與自我挑戰</a></li> +</ol> + +<h2 id="總結">總結</h2> + +<p>我們現在已經為我們的網站,創建了所有 "唯讀" 的頁面: 一個主頁,可以顯示每一個模組的實例數量,書本的列表與詳細信息頁面,書本的實例、作者、分類。沿著目前的學習路徑,我們學到了許多基本知識,有控制器、在非同步作業時管理流控制、使用 Pug 創建視圖、使用模型查詢數據庫、如何從視圖傳送信息到模板、如何創建並擴展模板。而完成挑戰的人,還會學到如何用 moment 處理日期。</p> + +<p>在下一篇文章,我們將依據目前為止學到的知識,創建HTML 表單以及表單管理代碼,開始修改儲存在網站中的資料。</p> + +<h2 id="參閱">參閱</h2> + +<ul> + <li><a href="http://caolan.github.io/async/docs.html">Async module</a> (Async docs)</li> + <li><a href="https://expressjs.com/en/guide/using-template-engines.html">Using Template engines with Express</a> (Express docs)</li> + <li><a href="https://pugjs.org/api/getting-started.html">Pug</a> (Pug docs)</li> + <li><a href="http://momentjs.com/docs/">Moment</a> (Moment docs)</li> +</ul> + +<p>{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/routes", "Learn/Server-side/Express_Nodejs/forms", "Learn/Server-side/Express_Nodejs")}}</p> + +<p> </p> + +<h2 id="本教學連結">本教學連結</h2> + +<ul> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction">Express/Node introduction</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment">Setting up a Node (Express) development environment</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">Express Tutorial: The Local Library website</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website">Express Tutorial Part 2: Creating a skeleton website</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose">Express Tutorial Part 3: Using a Database (with Mongoose)</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/routes">Express Tutorial Part 4: Routes and controllers</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express Tutorial Part 5: Displaying library data</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms">Express Tutorial Part 6: Working with forms</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/deployment">Express Tutorial Part 7: Deploying to production</a></li> +</ul> + +<p> </p> diff --git a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/locallibrary_base_template/index.html b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/locallibrary_base_template/index.html new file mode 100644 index 0000000000..c67e82f07e --- /dev/null +++ b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/locallibrary_base_template/index.html @@ -0,0 +1,71 @@ +--- +title: 本地圖書館基礎模板 +slug: Learn/Server-side/Express_Nodejs/Displaying_data/LocalLibrary_base_template +translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/LocalLibrary_base_template +--- +<p> </p> + +<p>現在我們了解如何使用 Pug 拓展模板,讓我們開始項目,創建一個基礎模板。這個模板會有一個側邊欄,連結到本教程中將要創建的各個頁面(例如,呈現並創建書本、種類、作者等等),以及一個主要內容區域,我們將在每個頁面中進行覆寫。</p> + +<p>開啟 <strong>/views/layout.pug </strong>,並以下列代碼,置換其內容。</p> + +<pre class="brush: html line-numbers language-html"><code class="language-html">doctype html +html(lang='en') + head + title= title + meta(charset='utf-8') + meta(name='viewport', content='width=device-width, initial-scale=1') + link(rel='stylesheet', href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css') + script(src='https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js') + script(src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js') + link(rel='stylesheet', href='/stylesheets/style.css') + body + div(class='container-fluid') + div(class='row') + div(class='col-sm-2') + block sidebar + ul(class='sidebar-nav') + li + a(href='/catalog') Home + li + a(href='/catalog/books') All books + li + a(href='/catalog/authors') All authors + li + a(href='/catalog/genres') All genres + li + a(href='/catalog/bookinstances') All book-instances + li + hr + li + a(href='/catalog/author/create') Create new author + li + a(href='/catalog/genre/create') Create new genre + li + a(href='/catalog/book/create') Create new book + li + a(href='/catalog/bookinstance/create') Create new book instance (copy) + + div(class='col-sm-10') + block content</code></pre> + +<p>此模板使用(並包含)來自 <a class="external external-icon" href="http://getbootstrap.com/" rel="noopener">Bootstrap</a> 的 JavaScript 和 CSS ,以改進 HTML 頁面的佈局和呈現方式。使用 Bootstrap 或其它客戶端網頁框架,是一種快速的方式,可以創建吸引人的網頁,能夠良好地適應不同的瀏覽器尺寸,並且允許我們處理頁面的呈現,而不需要糾纒於任何不同尺寸的細節—此處我們只想專注於伺服端代碼!</p> + +<p>佈局的安排應該相當明白,假如你已經閱讀了之前的 <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data#Template_primer">模板入門</a>。注意,使用 <code>block content</code> 當做定位符號,放到頁面內容將要放置的地方。</p> + +<p>基礎模板也參考了一個本地 css 檔 (<strong>style.css</strong>) ,此檔提供了一些額外的樣式。打開 <strong>/public/stylesheets/style.css</strong> ,並用底下的 CSS 代碼,取代它的內容:</p> + +<pre class="brush: css line-numbers language-css"><code class="language-css"><span class="selector token"><span class="class token">.sidebar-nav</span> </span><span class="punctuation token">{</span> + <span class="property token">margin-top</span><span class="punctuation token">:</span> <span class="number token">20</span>px<span class="punctuation token">;</span> + <span class="property token">padding</span><span class="punctuation token">:</span> <span class="number token">0</span><span class="punctuation token">;</span> + <span class="property token">list-style</span><span class="punctuation token">:</span> none<span class="punctuation token">;</span> +<span class="punctuation token">}</span></code></pre> + +<p>當我們開始運行網站時,我們應該看到側邊欄出現!在本教程的下個部分,我們將使用以上的佈局,來定義各個頁面。</p> + +<h2 id="下一步">下一步</h2> + +<ul> + <li>回到 <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express Tutorial Part 5: Displaying library data</a>.</li> + <li>繼續教學 5 下個章節: <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Home_page">Home page</a>.</li> +</ul> diff --git a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/template_primer/index.html b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/template_primer/index.html new file mode 100644 index 0000000000..af976b7155 --- /dev/null +++ b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/template_primer/index.html @@ -0,0 +1,149 @@ +--- +title: 模板入門 +slug: Learn/Server-side/Express_Nodejs/Displaying_data/Template_primer +translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Template_primer +--- +<p>模板是一個文字檔,定義了一個輸出檔的<em>結構</em>或者排版,使用定位符號表示,當模板被繪製時,資料將插入到何處(在<em>Express</em>,模板被稱為<em>視圖</em>)。</p> + +<h2 id="Express_模板選擇">Express 模板選擇</h2> + +<p>Express 可以與許多不同的<a href="https://expressjs.com/zh-tw/guide/using-template-engines.html">模板渲染引擎</a>一起使用。在本教程中,我們使用 <a class="external external-icon" href="https://pugjs.org/api/getting-started.html" rel="noopener">Pug</a>(以前稱為 <em>Jade</em>)作為模板。這是最流行的 Node 模板語言,並且官方將自身描述為 “用於編寫 HTML,語法乾淨且空格敏感,受 <a class="external external-icon" href="http://haml.info/" rel="noopener">Haml </a>影響很大”。</p> + +<p>不同的模板語言使用不同的方法,來定義佈局和標記數據的佔位符 — 一些使用 HTML 來定義佈局,而另一些則使用可以編譯為 HTML 的不同標記格式。 Pug 是第二種類型;它使用 HTML 的<em>表示形式</em>,其中任何行中的第一個單詞,通常表示HTML元素,後續行中的縮進,用於表示嵌套在這些元素中的任何內容。結果是一個頁面定義直接轉換為 HTML,但可以說更簡潔,更容易閱讀。</p> + +<div class="note"> +<p><strong>Note:</strong> The downside of using <em>Pug</em> is that it is sensitive to indentation and whitespace (if you add an extra space in the wrong place you may get an unhelpful error code). However once you have your templates in place, they are very easy to read and maintain.</p> +</div> + +<h2 class="highlight-spanned" id="Template_configuration"><span class="highlight-span">Template configuration</span></h2> + +<p>The <em>LocalLibrary</em> was configured to use <a class="external external-icon" href="https://pugjs.org/api/getting-started.html" rel="noopener">Pug</a> when we <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website">created the skeleton website</a>. You should see the pug module included as a dependency in the website's <strong>package.json</strong> file, and the following configuration settings in the <strong>app.js</strong> file. The settings tell us that we're using pug as the view engine, and that <em>Express</em> should search for templates in the <strong>/views</strong> subdirectory.</p> + +<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="comment token">// View engine setup.</span> +app<span class="punctuation token">.</span><span class="keyword token">set</span><span class="punctuation token">(</span><span class="string token">'views'</span><span class="punctuation token">,</span> path<span class="punctuation token">.</span><span class="function token">join</span><span class="punctuation token">(</span>__dirname<span class="punctuation token">,</span> <span class="string token">'views'</span><span class="punctuation token">)</span><span class="punctuation token">)</span><span class="punctuation token">;</span> +app<span class="punctuation token">.</span><span class="keyword token">set</span><span class="punctuation token">(</span><span class="string token">'view engine'</span><span class="punctuation token">,</span> <span class="string token">'pug'</span><span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre> + +<p>If you look in the views directory you will see the .pug files for the project's default views. These include the view for the home page (<strong>index.pug</strong>) and base template (<strong>layout.pug</strong>) that we will need to replace with our own content.</p> + +<pre><code>/express-locallibrary-tutorial //the project root + /views + error.pug + <strong>index.pug</strong> + layout.pug</code> +</pre> + +<h2 class="highlight-spanned" id="Template_syntax"><span class="highlight-span">Template syntax</span></h2> + +<p>The example template file below shows off many of Pug's most useful features.</p> + +<p>The first thing to notice is that the file maps the structure of a typical HTML file, with the first word in (almost) every line being an HTML element, and indentation being used to indicate nested elements. So for example, the <code>body</code> element is inside an <code>html</code> element, and paragraph elements (<code>p</code>) are within the <code>body</code> element, etc. Non-nested elements (e.g. individual paragraphs) are on separate lines.</p> + +<pre class="brush: html line-numbers language-html"><code class="language-html">doctype html +html(lang="en") + head + title= title + script(type='text/javascript'). + body + h1= title + + p This is a line with #[em some emphasis] and #[strong strong text] markup. + p This line has un-escaped data: !{'<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>em</span><span class="punctuation token">></span></span> is emphasised<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>em</span><span class="punctuation token">></span></span>'} and escaped data: #{'<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>em</span><span class="punctuation token">></span></span> is not emphasised<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>em</span><span class="punctuation token">></span></span>'}. + | This line follows on. + p= 'Evaluated and <span class="tag token"><span class="tag token"><span class="punctuation token"><</span>em</span><span class="punctuation token">></span></span>escaped expression<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>em</span><span class="punctuation token">></span></span>:' + title + + <span class="comment token"><!-- You can add HTML comments directly --></span> + // You can add single line JavaScript comments and they are generated to HTML comments + //- Introducing a single line JavaScript comment with "//-" ensures the comment isn't rendered to HTML + + p A line with a link + a(href='/catalog/authors') Some link text + | and some extra text. + + #container.col + if title + p A variable named "title" exists. + else + p A variable named "title" does not exist. + p. + Pug is a terse and simple template language with a + strong focus on performance and powerful features. + + h2 Generate a list + + ul + each val in [1, 2, 3, 4, 5] + li= val</code></pre> + +<p>Element attributes are defined in parentheses after their associated element. Inside the parentheses, the attributes are defined in comma- or whitespace- separated lists of the pairs of attribute names and attribute values, for example:</p> + +<ul> + <li><code>script(type='text/javascript')</code>, <code>link(rel='stylesheet', href='/stylesheets/style.css')</code></li> + <li><code>meta(name='viewport' content='width=device-width initial-scale=1')</code></li> +</ul> + +<p>The values of all attributes are <em>escaped</em> (e.g. characters like "<code>></code>" are converted to their HTML code equivalents like "<code>&gt;"</code>) to prevent injection of JavaScript/cross-site scripting attacks.</p> + +<p>If a tag is followed by the equals sign, the following text is treated as a JavaScript <em>expression</em>. So for example, in the first line below, the content of the <code>h1</code> tag will be <em>variable</em> <code>title</code> (either defined in the file or passed into the template from Express). In the second line the paragraph content is a text string concatented with the <code>title</code> variable. In both cases the default behaviour is to <em>escape</em> the line.</p> + +<pre class="brush: html line-numbers language-html"><code class="language-html">h1= title +p= 'Evaluated and <span class="tag token"><span class="tag token"><span class="punctuation token"><</span>em</span><span class="punctuation token">></span></span>escaped expression<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>em</span><span class="punctuation token">></span></span>:' + title</code></pre> + +<p>If there is no equals symbol after the tag then the content is treated as plain text. Within the plain text you can insert escaped and unescaped data using the <code>#{}</code> and<code> !{}</code> syntax, as shown below. You can also add raw HTML within the plain text.</p> + +<pre class="brush: html line-numbers language-html"><code class="language-html">p This is a line with #[em some emphasis] and #[strong strong text] markup. +p This line has an un-escaped string: !{'<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>em</span><span class="punctuation token">></span></span> is emphasised<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>em</span><span class="punctuation token">></span></span>'}, an escaped string: #{'<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>em</span><span class="punctuation token">></span></span> is not emphasised<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>em</span><span class="punctuation token">></span></span>'}, and escaped variables: #{title}.</code></pre> + +<div class="note"> +<p><strong>Tip:</strong> You will almost always want to escape data from users (via the <strong><code>#{}</code></strong> syntax). Data that can be trusted (e.g. generated counts of records, etc.) may be displayed without escaping the values.</p> +</div> + +<p>You can use the pipe ('<strong>|</strong>') character at the beginning of a line to indicate "<a class="external external-icon" href="https://pugjs.org/language/plain-text.html" rel="noopener">plain text</a>". For example, the additional text shown below will be displayed on the same line as the preceding anchor, but will not be linked.</p> + +<pre class="brush: html line-numbers language-html"><code class="language-html">a(href='http://someurl/') Link text +| Plain text</code></pre> + +<p>Pug allows you to perform conditional operations using <code>if</code>, <code>else</code> , <code>else if</code> and <code>unless</code>—for example:</p> + +<pre class="brush: html line-numbers language-html"><code class="language-html">if title + p A variable named "title" exists +else + p A variable named "title" does not exist</code></pre> + +<p>You can also perform loop/iteration operations using <code>each-in</code> or <code>while</code> syntax. In the code fragment below we've looped through an array to display a list of variables (note the use of the 'li=' to evaluate the "val" as a variable below. The value you iterate across can also be passed into the template as a variable!</p> + +<pre class="brush: html line-numbers language-html"><code class="language-html">ul + each val in [1, 2, 3, 4, 5] + li= val</code></pre> + +<p>The syntax also supports comments (that can be rendered in the output—or not—as you choose), mixins to create reusable blocks of code, case statements, and many other features. For more detailed information see <a class="external external-icon" href="https://pugjs.org/api/getting-started.html" rel="noopener">The Pug docs</a>.</p> + +<h2 class="highlight-spanned" id="Extending_templates"><span class="highlight-span">Extending templates</span></h2> + +<p>Across a site, it is usual for all pages to have a common structure, including standard HTML markup for the head, footer, navigation, etc. Rather than forcing developers to duplicate this "boilerplate" in every page, <em>Pug</em> allows you to declare a base template and then extend it, replacing just the bits that are different for each specific page.</p> + +<p>For example, the base template <strong>layout.pug</strong> created in our <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website">skeleton project</a> looks like this:</p> + +<pre class="brush: html line-numbers language-html"><code class="language-html">doctype html +html + head + title= title + link(rel='stylesheet', href='/stylesheets/style.css') + body + block content</code></pre> + +<p>The <code>block</code> tag is used to mark up sections of content that may be replaced in a derived template (if the block is not redefined then its implementation in the base class is used).</p> + +<p>The default <strong>index.pug</strong> (created for our skeleton project) shows how we override the base template. The <code>extends</code> tag identifies the base template to use, and then we use <code>block <em>section_name</em></code> to indicate the new content of the section that we will override.</p> + +<pre class="brush: html line-numbers language-html"><code class="language-html">extends layout + +block content + h1= title + p Welcome to #{title}</code></pre> + +<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/LocalLibrary_base_template">The LocalLibrary base template</a>.</li> +</ul> |
