diff options
author | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:42:52 -0500 |
---|---|---|
committer | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:42:52 -0500 |
commit | 074785cea106179cb3305637055ab0a009ca74f2 (patch) | |
tree | e6ae371cccd642aa2b67f39752a2cdf1fd4eb040 /files/ru/learn/server-side/express_nodejs/displaying_data | |
parent | da78a9e329e272dedb2400b79a3bdeebff387d47 (diff) | |
download | translated-content-074785cea106179cb3305637055ab0a009ca74f2.tar.gz translated-content-074785cea106179cb3305637055ab0a009ca74f2.tar.bz2 translated-content-074785cea106179cb3305637055ab0a009ca74f2.zip |
initial commit
Diffstat (limited to 'files/ru/learn/server-side/express_nodejs/displaying_data')
10 files changed, 976 insertions, 0 deletions
diff --git a/files/ru/learn/server-side/express_nodejs/displaying_data/author_list_page/index.html b/files/ru/learn/server-side/express_nodejs/displaying_data/author_list_page/index.html new file mode 100644 index 0000000000..2e1edbc625 --- /dev/null +++ b/files/ru/learn/server-side/express_nodejs/displaying_data/author_list_page/index.html @@ -0,0 +1,85 @@ +--- +title: Список авторов. Тест - список жанров +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="Контроллер"><span class="highlight-span">Контроллер</span></h2> + +<p>Функция контроллера списка авторов должна получить список всех элементов в <code>Author</code> , и передать этот список в шаблон для отображения.</p> + +<p>Откройте файл <strong>/controllers/authorController.js</strong>. Найдите экспортируемый метод <code>author_list()</code> в начале файла и замените его следующим ниже кодом:</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>Метод использует такие функции модели как <code>find()</code>, <code>sort()</code> и <code>exec()</code> для того, чтобы вернуть все объекты <code>Author</code> отсортированными по <code>family_name</code> в алфавитном порядке. В вызове <code>exec()</code> callback-функция имеет первый параметр- объект ошибок (или <code>null</code>) и второй параметр - список всех авторов, если ошибок не было. При ошибках вызывается следующая функция промежуточного слоя с полученным значением объекта ошибок, а если ошибок не было, отображается шаблон <strong>author_list</strong>(.pug), передавая странице <code>title</code> и список авторов (<code>author_list</code>).</p> + +<h2 class="highlight-spanned" id="Представление"><span class="highlight-span">Представление</span></h2> + +<p>Создайте файл <strong>/views/author_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 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>Представление создано по тому же образцу, что и другие шаблоны.</p> + +<h2 class="highlight-spanned" id="Как_это_выглядит"><span class="highlight-span">Как это выглядит?</span></h2> + +<p>Запустите приложение и откройте браузер с адресом <a class="external external-icon" href="http://localhost:3000/" rel="noopener">http://localhost:3000/</a>. Выберите ссылку <em>All authors</em>. Если все было сделано правильно, страница должна выглядеть примерно нак, как на следующем скриншоте.</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>Заметка:</strong> Представление дат продолжительности жизни автора выгядит безобразно! Это можно исправить, если использовать <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data#date_formatting">тот же подход</a> , который применялся для списка <code>BookInstance</code> (добавить в модель <code>Author</code> виртуальное свойство продолжительности жизни). Но в этот раз, однако, некоторые даты могут отсутствовать, и ссылки на несуществующие свойства игнорируются, если не задан строгий режим. Метод <code>moment()</code> возврашает текущее время, и нежелательно, чтобы отсутствующие даты форматировались как "сегодня". Один из способов состоит в том, чтобы форматирующая функция возвращала пустую строку, если дата не существует. Например:</p> + +<p><code>return this.date_of_birth ? moment(this.date_of_birth).format('YYYY-MM-DD') : '';</code></p> +</div> + +<h2 id="Тест_-_страница_списка_жанров!Edit">Тест - страница списка жанров!<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>В этой части требуется создать собственную страницу списка жанров. Страница должна показывать жанры, имеющиеся в БД, а для каждого жанра должна быть создана ссылка на страницу с детальной информацией. Скриншот ожидаемого результата приводится ниже.</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>Функция контроллера списка жанров должна получить список всех экземпляров <code>Genre</code>, и передать его в шаблон для отображения.</p> + +<ol> + <li>Следует отредактировать <code>genre_list()</code> в файле <strong>/controllers/genreController.js</strong>. </li> + <li>Реализация почти такая же, как и для контроллера <code>author_list()</code> . + <ul> + <li>Sort the results by name, in ascending order.</li> + </ul> + </li> + <li>Отображающий шаблон должен быть назван <strong>genre_list.pug</strong>.</li> + <li>Шаблону для отображения должны быть переданы переменные <code>title</code> (строка 'Genre List') и <code>genre_list</code> (the list of список жанров, который вернет callback-функция <code>Genre.find()</code>.</li> + <li>Представление должно соответствовать скриншоту, приведенному ранее (оно должно иметь структуру и формат, похожие на таковые в представлении списка авторов, за исключением, конечно, продолжительности жизни, так как для жанров даты не заданы).</li> +</ol> + +<h2 id="Далее">Далее</h2> + +<p>Вернуться к части 5 - <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express Tutorial Part 5: Displaying library data</a>.</p> + +<p>Перейти к следующему подразделу в части 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/ru/learn/server-side/express_nodejs/displaying_data/book_list_page/index.html b/files/ru/learn/server-side/express_nodejs/displaying_data/book_list_page/index.html new file mode 100644 index 0000000000..b5a4400d90 --- /dev/null +++ b/files/ru/learn/server-side/express_nodejs/displaying_data/book_list_page/index.html @@ -0,0 +1,68 @@ +--- +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="Контроллер"><span class="highlight-span">Контроллер</span></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>_id</code> и виртуальные поля). Здесь мы также вызываем <code>populate()</code> on <code>Book</code>, указывая поле <code>author</code> —это заменит сохраненный идентификатор автора книги полными сведениями об авторе.</p> + +<p>При успешном выполнении, обратный вызов передаст запрос на отрисовку шаблона <strong>book_list</strong>(.pug), передаст <code>title</code> и<code>book_list</code> (список книг с автором) в качестве переменных.</p> + +<h2 class="highlight-spanned" id="Представление"><span class="highlight-span">Представление</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>View расширит базовый шаблон <strong>layout.pug</strong> и переопределит <code>block</code> с именем '<strong>content</strong>'. Он отображает <code>title</code> который мы передали из контроллера (с помощью метода <code>render()</code> ), а затем перебирает переменную <code>book_list</code> используя синтаксис <code>each</code>-<code>in</code>-<code>else</code> . Для каждой книги создается элемент списка, отображающий название книги в виде ссылки на страницу сведений о книге, за которой следует имя автора. Если в <code>book_list</code> нет книг, то выполняется <code>else</code>, и отображается текст "нет книг".'</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="https://developer.mozilla.org/en-US/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><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="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/BookInstance_list_page">BookInstance list page</a>.</li> +</ul> diff --git a/files/ru/learn/server-side/express_nodejs/displaying_data/bookinstance_list_page/index.html b/files/ru/learn/server-side/express_nodejs/displaying_data/bookinstance_list_page/index.html new file mode 100644 index 0000000000..512e78d040 --- /dev/null +++ b/files/ru/learn/server-side/express_nodejs/displaying_data/bookinstance_list_page/index.html @@ -0,0 +1,69 @@ +--- +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>Book</code>, с которой связаны экземпляры <code>BookInstance</code> (linked to its detail page), а такжде дополнительнцю информацию, имеющуюся в модели <code>BookInstance</code>, включая статус, издание, и уникальный идентификатор каждой копии. Уникальное значение идентификатора копии должно быть связано со страницей детальной информации <code>BookInstance</code>.</p> + +<h2 class="highlight-spanned" id="Контроллер"><span class="highlight-span">Контроллер</span></h2> + +<p>Функция контроллера списка <code>BookInstance</code> требуется для получения списка всех экземпляров некоторой книги, для получения информации, связанной с книгой, и для передачиполученного списка в шаблог для отображения.</p> + +<p>Откройте файл <strong>/controllers/bookinstanceController.js</strong>. Найдите экспортируемый метод <code>bookinstance_list()</code> контроллера и замените его следующим кодом (измененный код выделен жирным).</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> + + <strong>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></strong> + +<span class="punctuation token">}</span><span class="punctuation token">;</span></code></pre> + +<p>Чтобы вернуть все объекты <code>BookInstance,</code> метод использует функцию <code>find()</code> модели. Далее в цепочке вызывается метод <code>populate()</code> с аргументом - полем <code>book,</code> что приводит к замене идентификатора id, хранящегося для каждого экземпляра <code>BookInstance</code> полным документом <code>Book</code>.</p> + +<p>При удаче, callback-функция, переданная запросу, заполняет шаблон <strong>bookinstance_list</strong>(.pug), передав переменные <code>title</code> и <code>bookinstance_list</code>.</p> + +<h2 class="highlight-spanned" id="Представление">Представление</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>This view is much the same as all the others. It extends the layout, replacing the <em>content</em> block, displays the <code>title</code> passed in from the controller, and iterates through all the book copies in <code>bookinstance_list</code>. For each copy we display its status (colour coded) and if the book is not available, its expected return date. One new feature is introduced—we can use dot notation after a tag to assign a class. So <code>span.text-success</code> will be compiled to <code><span class="text-success"></code> (and might also be written in Pug as <code>span(class="text-success")</code>.</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, open your browser to <a class="external external-icon" href="http://localhost:3000/" rel="noopener">http://localhost:3000/</a>, then select the <em>All book-instances </em>link. If everything is set up correctly, your site should look something like the following screenshot.</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="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/Date_formatting_using_moment">Date formatting using moment</a>.</li> +</ul> diff --git a/files/ru/learn/server-side/express_nodejs/displaying_data/date_formatting_using_moment/index.html b/files/ru/learn/server-side/express_nodejs/displaying_data/date_formatting_using_moment/index.html new file mode 100644 index 0000000000..58f297ce95 --- /dev/null +++ b/files/ru/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 List </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> Можно применять <em>moment</em> для форматирования непосредственно в шаблонах Pug, а можно отформатировать строку в других местах. Использование виртуального свойства позволяет получить дату, отформатированную точно так же, как при помощи <code>due_date</code>. </p> +</div> + +<h2 class="highlight-spanned" id="Установка_moment"><span class="highlight-span">Установка moment</span></h2> + +<p>Ведите следующую команду в корне проекта:</p> + +<pre class="brush: bash line-numbers language-bash"><code class="language-bash">npm install moment</code></pre> + +<h2 class="highlight-spanned" id="Создание_виртуального_свойства"><span class="highlight-span">Создание виртуального свойства</span></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>Добавьте виртуальное свойство <code>due_back_formatted</code> сразу после свойства url.</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> Метод format method может вывести дату почти по любому образцу. Синтаксис для представления различных составляющих даты можно найти в документации ( <a class="external external-icon" href="http://momentjs.com/docs/#/displaying/" rel="noopener">moment documentation</a>).</p> +</div> + +<h2 class="highlight-spanned" id="Обновляем_представление"><span class="highlight-span">Обновляем представление</span></h2> + +<p>Откройте файл <strong>/views/bookinstance_list.pug</strong> и замените <code>due_back</code> на <code>due_back_formatted</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> + +<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/Author_list_page">Author list page and Genre list page challenge</a> (страница списка авторов и тест- страница списка жанров).</li> +</ul> + +<p> </p> diff --git a/files/ru/learn/server-side/express_nodejs/displaying_data/flow_control_using_async/index.html b/files/ru/learn/server-side/express_nodejs/displaying_data/flow_control_using_async/index.html new file mode 100644 index 0000000000..32100db740 --- /dev/null +++ b/files/ru/learn/server-side/express_nodejs/displaying_data/flow_control_using_async/index.html @@ -0,0 +1,138 @@ +--- +title: Асинхронное управление потоками при помощи async +slug: Learn/Server-side/Express_Nodejs/Displaying_data/flow_control_using_async +tags: + - Node + - Часть 5 +translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/flow_control_using_async +--- +<p>Код контроллера для некоторых страниц библиотеки будет зависеть от результатов многих асинхронных запросов, которые должны выполняться в определенном порядке или параллельно. Для того, чтобы управлять потоком выполнения, и выводить страницы, когда получена вся необходимая информация, будет использован <a class="external external-icon" href="https://www.npmjs.com/package/async" rel="noopener">async</a> - известный модуль node.</p> + +<div class="note"> +<p><strong>Note:</strong> В JavaScript существует много других способов управления аснхронным поведением и потоком выполнения, включая такой относительно новый элемент языка JacaScript как <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Techniques/Promises">Promises</a> (обещания, промисы).</p> +</div> + +<p>Модуль Async имеет массу полезных методов (см. документациюt <a class="external external-icon" href="http://caolan.github.io/async/docs.html" rel="noopener">the documentation</a>). Вот некоторые наиболее важные функции:</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="Почему_это_необходимо"><span class="highlight-span">Почему это необходимо?</span></h2> + +<p>Большинство методов, которые используются в <em>Express</em> - <span class="highlight-span">асинхронные - вы определяете выполняемую операцию, передавая </span> callback-функцию. Метод завершается немедленно, а callback-функция вызывается тогда, когда завершилась запрошенная операция. По соглашению, принятому в <em>Express</em>, callback-функция передает значение ошибки <em>error</em> как первый параметр (или <code>null</code> при успехе) и результат функции (если есть) как второй параметр.</p> + +<p>Если контроллер должен выполнить только одну асинхронную операцию, чтобы получить информацию для представления страницы, то реализация проста - мы просто представляем шаблон в колбэке. Фрагмент кода (ниже) демонстрирует это для функции, которая подсчитывает количество элементов модкли <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) { + // ... сделать что-то, если ошибка + + // При успехе представить результат, передав count в render-функцию (здесь - как переменную 'data'). + res.render('the_template', { data: count } ); + }); +<code>}</code> +</pre> + +<p>Однако что, если требуется сделать <strong>множественные</strong> асинхронные запросы, и результат нельзя представить, пока не завершились все операции? Наивная реализация могла бы использовать "венок" запросов, запуская последующие запросы в колбэках предыдущих, и представляя ответ в последнем колбэке. Проблема такого подхода состоит в том, что запросы должны вапольняться последовательно, хотя, вероятно, было бы более эффективно выполнять их параллельно. Это также может привести к усложненному вложенному коду, что обычно называют адом обратных вызовов ( <a class="external external-icon" href="http://callbackhell.com/" rel="noopener">callback hell</a> ).</p> + +<p>Намного лучше было бы выполнять все запросы параллельно, и иметь единственную callback-функцию, которая будет вызвана после того как все запросы выполнены. Именно такое выполнение операций модуль <em>Async</em> делает легким и простым!</p> + +<h2 class="highlight-spanned" id="Параллельные_асинхронные_операции"><span class="highlight-span">Параллельные асинхронные операции</span></h2> + +<p>Метод <code><a class="external external-icon" href="http://caolan.github.io/async/docs.html#parallel" rel="noopener">async.parallel()</a></code> используется для параллельного выполнения нескольких асинхронных операций.</p> + +<p>Первый аргумент в <code>async.parallel()</code> - это коллекция асинхронных функций, которые требуется выполнить (массив, объект или другой итерируемый элемент). Каждая функция получает callback-функцию <code>callback(err, result)</code> , которую она должна вызвать при завершении, с ошибкой <code>err</code> (может быть <code>null</code>) и, возможно, со значением результата <code>results</code>.</p> + +<p>Возможный второй аргумент для <code>async.parallel()</code> - это callback -функция, которая должна быть вызвана после завершения всех функций, указанных в первом аргументе. Эта функция вызывается с аргументом ошибки и результатом - коллекцией результатов отдельных асинхронных операций. Тип коллекции - такой же, как и тип первого аргумента async.parallel (т.е. если передается <em>массив</em> асинхронных функций, итоговая callback-функция будет вызвана с <em>массивом</em> результатов). Если любая из параллельных функций сообщила об ошибке, сразу вызывается итоговая callback-функция, которая возвращает ошибку.</p> + +<p>Пример ниже показывает, как это работает в случае, когда первый аргумент является объектом. Как видно, результаты возвращаются в объекте с такими же именами свойств, как у переданных функций.</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' равны: {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>Если вместо объекта передать <em>массив </em>функций как первый аргумент, результатом будет массив (порядок результатов в массиве такой же, как и порядок функций в массиве, а не порядок выполнения функций).</p> + +<h2 class="highlight-spanned" id="Последовательные_асинхронные_операции"><span class="highlight-span">Последовательные асинхронные операции</span></h2> + +<p>Для выполнения нескольких асинхронных операций последовательно используется метод <code><a class="external external-icon" href="http://caolan.github.io/async/docs.html#series" rel="noopener">async.series()</a></code> , при этом <span class="highlight-span">последующие функции не зависят от результатов предыдущих функций</span>. Метод определяется и ведет себя так же, как и <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>Заметка:</strong> Спецификация языка ECMAScript (JavaScript) устанавливает, что порядок в перечислении объектов не определен, поэтому возможно, что функции не будут вызываться в том порядке, в котором вы их задали на всех платформах. Если порядок вызова действительно важен, вместо объекта следует передавать массив, как показано ниже.</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="Последовательные_зависимые_асинхронные_операции"><span class="highlight-span">Последовательные зависимые асинхронные операции</span></h2> + +<p>Выполнение нескольких асинхронных операций последовательно, когда каждая операция зависит от результатов предыдущих операций, осуществляется методом <code><a class="external external-icon" href="http://caolan.github.io/async/docs.html#waterfall" rel="noopener">async.waterfall()</a></code>.</p> + +<p>Функции-callback, которая вызываются асинхронными функциями , содержит <code>null</code> как первый аргумент, и результаты в следующих аргументах. Каждая функция в последовательности (кроме первой) как аргументы использует результаты предыдущих функция, а callback-функция является последним аргументом. Когда операции завершаются, вызывается финальная callback-функция, аргументы которой - объект err и результат последней операции. Как это работает, станет более ясным после рассмотрения примера - фрагмента кода, приведенного ниже ( пример взят из документации <em>async</em>):</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">;//результаты 'one' и 'two'</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 равен 'one' , arg2 равен '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> //результат 'three' + <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 равен '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">; //результат 'done'</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 равен 'done'</span> +<span class="punctuation token">}</span> +<span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre> + +<h2 class="highlight-spanned" id="Установка_async"><span class="highlight-span">Установка async</span></h2> + +<p>Установим модуль async при помощи менеджера пакетов NPM, чтобы использовать его в своем коде. Это делается обычным способом - откроем окно команд в корне проекта <em>LocalLibrary</em> и введем команду:</p> + +<pre class="brush: bash line-numbers language-bash"><code class="language-bash">npm install async</code></pre> + +<h2 id="Дальнейшие_шаги">Дальнейшие шаги</h2> + +<ul> + <li>Вернуться учебнику <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express, Part 5: Вывести данные библиотеки</a>.</li> + <li>Перейти к следующему разделу части 5: <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Template_primer">Основы шаблонов</a>.</li> +</ul> diff --git a/files/ru/learn/server-side/express_nodejs/displaying_data/genre_detail_page/index.html b/files/ru/learn/server-side/express_nodejs/displaying_data/genre_detail_page/index.html new file mode 100644 index 0000000000..be5bd57962 --- /dev/null +++ b/files/ru/learn/server-side/express_nodejs/displaying_data/genre_detail_page/index.html @@ -0,0 +1,121 @@ +--- +title: Страница с подробностями жанров +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>detail)</em> для жанров должна показывать информацию для отдельного жанра по его автоматически генерируему идентификатору <code>_id</code>. Должно быть показано название жанра и список книг этого жанра, со ссылками на страницу с детальной информацией для каждой книги.</p> + +<h2 id="Controller">Controller</h2> + +<p>Откройте файл<em> </em><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>Найдите экспортируемый метод контроллера <code>genre_detail</code><code>()</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>Метод использует <code>async.parallel()</code> для параллельного запроса названия жанра и связанных с ним книг, причем callback-функция возвращает страницу, когда (если) оба запроса завершились успешно.</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/ru/learn/server-side/express_nodejs/displaying_data/home_page/index.html b/files/ru/learn/server-side/express_nodejs/displaying_data/home_page/index.html new file mode 100644 index 0000000000..248187f1a5 --- /dev/null +++ b/files/ru/learn/server-side/express_nodejs/displaying_data/home_page/index.html @@ -0,0 +1,134 @@ +--- +title: Home page +slug: Learn/Server-side/Express_Nodejs/Displaying_data/Home_page +translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Home_page +--- +<p>Первой создаваемой страницей будет домашняя страница веб-сайта, доступная из корня сайта (<code>'/'</code>) или из каталога (<code>catalog/</code>). На странице будет виден статический текст, описывающий сайт, и динамически вычисляемые "количества" записей разных типов имеющихся в БД.</p> + +<p>Маршрут для домашней страницы уже создан. Для завершения страницы обновить функции контроллера, чтобы он извлекал количество записей из БД, и создавал представление (шаблон), который можно использовать для презентации страницы.</p> + +<h2 id="Маршрут">Маршрут</h2> + +<p>Маршруты индексной страницы созданы ранее в предыдущем разделе (<a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/routes">previous tutorial).</a> Напомним, все функции маршрутов определены в файле <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>Параметр callback-функции определен в <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>Именно эту функцию контроллера мы расширим, чтобы получать информацию из моделей и затем отображать ее, используя шаблоны (представления).</p> + +<h2 id="Контроллер">Контроллер</h2> + +<p>Функция контроллера индекса должна получать информацию о том, сколько книг (<code>Book)</code>, экземпляров книг (<code>BookInstance)</code>, сколько из них доступно, сколько авторов (<code>Author)</code>, жанров (<code>Genre)</code> имеется в БД, должна поместить эту информацию в шаблон, чтобы создать HTML-страницу, после чего вернуть ее в HTTP-ответе.</p> + +<div class="note"> +<p><strong>Заметка:</strong> Количество экземпляров в каждой модели вычисляется при помощи метода <code><a class="external external-icon" href="http://mongoosejs.com/docs/api.html#model_Model.countDocuments" rel="noopener">countDocuments()</a></code> . Он вызывается для модели с возможным набором условий, необходимых для проверки соответствия первому аргументу и callback-функции второго аргумента (обсуждалось ранее в "Использование базы данных с Mongoose" <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose">Using a Database (with Mongoose)</a>), причем можно вернуть также запрос <code>Query,</code> а затем выполнить его позже при помощи callback. Эта callback-функция будет выполняться, когда БД вернет количество записей. Значение ошибки (or <code>null</code>) будет первым параметром, а количество записей (или null, если была ошибка) - вторым параметром.</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>Откройте файл <strong>/controllers/bookController.js</strong>. Почти в самом начале вы должны увидеть экспортируемую функцию <code>index()</code> .</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>Замените весь код, показанный выше, на следующий фрагмент кода. Первое, что он делает - импортирует (<code>require()</code>) все модели (выделено жирным). Это требуется, поскольку они нужны для подсчета числа записей. Затем импортируется модуль <em>async</em> .</p> + +<pre class="brush: js "><strong>var Book = require('../models/book'); +var Author = require('../models/author'); +var Genre = require('../models/genre'); +var BookInstance = require('../models/bookinstance');</strong> + +var async = require('async'); + +exports.index = function(req, res) { + + async.parallel({ + book_count: function(callback) { + Book.count<s>Documents</s>({}, callback); // Pass an empty object as match condition to find all documents of this collection +// count<s>Documents</s> не работает, работает только просто count + }, + book_instance_count: function(callback) { + BookInstance.count<s>Documents</s>({}, callback); + }, + book_instance_available_count: function(callback) { + BookInstance.count<s>Documents</s>({status:'Available'}, callback); + }, + author_count: function(callback) { + Author.count<s>Documents</s>({}, callback); + }, + genre_count: function(callback) { + Genre.count<s>Documents</s>({}, callback); + } + }, function(err, results) { + res.render('index', { title: 'Local Library Home', error: err, data: results }); + }); +};</pre> + +<p>Метод <code>async.parallel()</code> передает объект с функциями для получения количества элементов каждой модели. Все эти функции стартуют одновременно. Когда все они завершатся, будет вызвана финальная callback-функция, в итоговом параметре которой содержится нужный нам результат (или ошибка).</p> + +<p>При успешном завершении callback-функции она вызывает <code><a class="external external-icon" href="http://expressjs.com/en/4x/api.html#res.render" rel="noopener">res.render()</a></code>, у которой в качестве параметров - представление (шаблон) '<strong>index</strong>' и объект, содержащий данные, которые следует поместить в шаблон (среди них - количества элементов в моделях). Данные представлены как пары ключ-значение, и могут быть получены в шаблоне по ключу.</p> + +<div class="note"> +<p><strong>Заметка:</strong> В данном случае callback-функция, которую вызывает <code>async.parallel()</code> , несколько необычная - страница отображается всегда, независимо от того, была ошибка или нет (обычно используют отдельный путь выполнения для обработки выводимых ошибок).</p> +</div> + +<h2 id="Представление">Представление</h2> + +<p>Откройте файл <strong>/views/index.pug</strong> и замените его содержимое текстом, приведенным ниже</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>Представление несложное. Мы расширили базовый шаблон <strong>layout.pug</strong>, переопределив блок (<code>block)</code> с именем '<strong>content</strong>'. Первый заголовок <code>h1</code> будет экранированным текстом - значением переменной <code>title</code> ,variable that которая передается в функцию <code>render()</code> —заметьте, что применение '<code>h1=</code>' говорит, что следующий текст рассматривается как выражение JavaScript. Затем расположен параграф, знакомящий с LocalLibrary.</p> + +<p>Под заголовком <em>Dynamic content</em> мы проверяем, определена ли переданная из функции <code>render()</code> переменная error. Если да, отмечаем ошибку. Если нет, выводим ( как список) количества копий каждой модели, которые хранятся в переменной <code>data</code>.</p> + +<div class="note"> +<p><strong>Заметка:</strong> Мы не экранируем количества элементов (т.е. используется синтаксис <code>!{}</code> ) потому что эти значения вычисляются. Если бы информация предоставлялась конечным пользователем, следовало бы экранировать переменную перед выводом.</p> +</div> + +<h2 id="Как_это_выглядит">Как это выглядит?</h2> + +<p>Сейчас у нас есть все для того, чтобы показать страницу index. Запустите приложение и откройте браузер с адресом <a class="external external-icon" href="http://localhost:3000/" rel="noopener">http://localhost:3000/</a>. Если все задано правильно, ваш сайт должен иметь примерно такой вид, как на приведенном снимке экрана.</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>Заметка:</strong> Элементы бокового меню использовать еще нельзя, так как адреса, представления и шаблоны для этих страниц еще не определены. Если вы попытаетесь их использовать, будет выведено сообщение об ошибке, например, вида "NOT IMPLEMENTED: Book list" (НЕ РЕАЛИЗОВАНО: список книг), в зависимости от выбранного элемента меню. Эти строковые литералы (которые будут замещены действительными данными) были заданы в различных файлах контроллеров в каталоге "controllers".</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/ru/learn/server-side/express_nodejs/displaying_data/index.html b/files/ru/learn/server-side/express_nodejs/displaying_data/index.html new file mode 100644 index 0000000000..bb2e804d2e --- /dev/null +++ b/files/ru/learn/server-side/express_nodejs/displaying_data/index.html @@ -0,0 +1,83 @@ +--- +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">Теперь мы готовы добавить страницы, на которых будут отображаться книги веб-сайта <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">LocalLibrary</a> и другие данные. Страницы будут включать главную страницу, которая показывает сколько записей определенного типа мы имеем и отдельные страницы для детального просмотра записей. Попутно мы приобретем практический опыт в получении записей из баз данных и использовании шаблонов.</p> + +<table class="learn-box standard-table"> + <tbody> + <tr> + <th scope="row">Предварительные знания:</th> + <td>Завершите изучение предыдущих тем учебника (включая <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/routes">Учебник Express часть 4: Маршруты и контроллеры</a>).</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 модели</a>, которые можно использовать для взаимодействия с базой данных и создания некоторых исходных записей библиотеки. Затем мы <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/routes">создали все маршруты</a>, необходимые для веб-сайта LocalLibrary, но с "фиктивными" функциями контроллеров (это скелетные функции, которые просто возвращают сообщение "не реализовано " при доступе к странице).</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">Aсинхронное управление потоками с помощью 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">Базовые шаблоны LocalLibrary</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">Форматирование даты с момента использования</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, запросе базы данных с помощью наших моделей, как передавать информацию в шаблон из вашего представления, а также как создавать и расширять шаблоны. Те, кто выполнил вызов также узнали немного о дате обработки с помощью момента.</p> + +<p>В нашей следующей статье мы будем опираться на наши знания, создавая HTML-формы и код обработки форм, чтобы начать изменять данные, хранящиеся на сайте.</p> + +<h2 id="Смотрите_так_же">Смотрите так же</h2> + +<ul> + <li><a href="http://caolan.github.io/async/docs.html">Aссинхроный модуль</a> (Асинхронные документация)</li> + <li><a href="https://expressjs.com/en/guide/using-template-engines.html">Использование механизмов шаблонов с Express</a> (Express документация)</li> + <li><a href="https://pugjs.org/api/getting-started.html">Pug</a> (Pug документация)</li> + <li><a href="http://momentjs.com/docs/">Moment</a> (Moment документация)</li> +</ul> + +<p>{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/routes", "Learn/Server-side/Express_Nodejs/forms", "Learn/Server-side/Express_Nodejs")}}</p> + +<h2 id="In_this_module">In this module</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> diff --git a/files/ru/learn/server-side/express_nodejs/displaying_data/locallibrary_base_template/index.html b/files/ru/learn/server-side/express_nodejs/displaying_data/locallibrary_base_template/index.html new file mode 100644 index 0000000000..13c61ea6cb --- /dev/null +++ b/files/ru/learn/server-side/express_nodejs/displaying_data/locallibrary_base_template/index.html @@ -0,0 +1,69 @@ +--- +title: Базовый шаблон LocalLibrary +slug: Learn/Server-side/Express_Nodejs/Displaying_data/LocalLibrary_base_template +translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/LocalLibrary_base_template +--- +<p>Теперь, чтобы мы понимали как расширить шаблон с помощью Pug, давайте создадим базовый шаблон для проекта. У него будет боковая панель (sidebar)) со ссылками на страницы, которые мы надеемся создать на протяжении учебника (например, для отображения и создания книг, жанров, автор иов т. д.) и основная область контента, которую мы переопределим на каждой из отдельных страниц.</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>Шаблон использует (и включает) JavaScript и CSS из <a class="external external-icon" href="http://getbootstrap.com/" rel="noopener">Bootstrap</a> , что позволяет улучшить макет и представление HTML-страницы. Применение Bootstrap или другого клиентского фреймворка - быстрый способ создать привлекательную, хорошо масштабируемую страницу. Кроме того, это позволяет получить представление страницы, не вдаваясь в детали - мы можем уделить все внимание коду на стороне сервера!</p> + +<p>Макет представляется достаточно очевидным, если Вы уже прочли статью Основы шаблонов (<a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data#Template_primer">Template primer</a>) выше. Обратите внимание на использование <code>block content</code> в качестве места для размещения контента отдельных страниц.</p> + +<p>Базовый шаблон также ссылается на локальный файл стилей (<strong>style.css</strong>), что обеспечивает дополнительное управление внешним видом. Откройте <strong>/public/stylesheets/style.css</strong> и замените его содержимое таким текстом:</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="https://developer.mozilla.org/ru/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Учебник Express часть 5: Отображение данных библиотеки</a>.</li> + <li>Перейти к следующему подразделу <a href="https://developer.mozilla.org/ru/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Home_page">Домашняя страница</a>.</li> +</ul> diff --git a/files/ru/learn/server-side/express_nodejs/displaying_data/template_primer/index.html b/files/ru/learn/server-side/express_nodejs/displaying_data/template_primer/index.html new file mode 100644 index 0000000000..3f537db354 --- /dev/null +++ b/files/ru/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 class="external external-icon" href="https://expressjs.com/en/guide/using-template-engines.html" rel="noopener">template rendering engines</a>). В этом руководстве для шаблонов будет использован <a class="external external-icon" href="https://pugjs.org/api/getting-started.html" rel="noopener">Pug</a> (ранее известный как Jade) . Это наиболее популярный в Node язык шаблонов, который о себе заявляет так: чистый, чувствительный к пробелам синтаксис для написания HTML, на который сильно повлиял <a class="external external-icon" href="http://haml.info/" rel="noopener">Haml</a>.</p> + +<p>Разные языки шаблонов используют различные подходы для определения внешнего вида и разметки позиций для данных—некоторые используют HTML для определения внешнего вида, тогда как другие применяют различные форматы разметки, которые затем должы компилироваться в HTML. Pug - второго типа; он использует <em>представление</em> (<em>representation) </em> HTML, в котором первое слово в каждой строке обычно представляет элемент HTML, а отступы в следующих строках применяются, чтобы представить вложенные элементы. Результатом является определение страницы, которое транслируется непосредственно в HTML, и которое, вероятно, более краткое и легче читается.</p> + +<div class="note"> +<p><strong>Заметка:</strong> недостаток применения <em>Pug</em> - это чувствительность к отступам и пробелам (если добавить лишний пробел в "плохом" месте, можно получить невразумительный код ошибки). Однако, если ваши шаблоны уже действуют, их очень легко читать и поддерживать.</p> +</div> + +<h2 class="highlight-spanned" id="Конфигурация_шаблона"><span class="highlight-span">Конфигурация шаблона</span></h2> + +<p>Когда создавался каркас (<a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website">the skeleton website</a>) веб-сайта <em>LocalLibrary, </em>он был настроен на использование <a class="external external-icon" href="https://pugjs.org/api/getting-started.html" rel="noopener">Pug</a> . Можно было заметить, что модуль pug включен в зависимости в файле <strong>package.json</strong>, и установлен (app.set(...)) как движок представлений в файле <strong>app.js</strong>. Эта установка показывает,, что движок представлений - pug, и что <em>Express</em> должен искать шаблоны в подкаталоге <strong>/views</strong>.</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>Если посмотреть содержимое каталога <strong>views</strong>, можно увидеть файлы с расширением .pug, в которых шаблоны представлений по умолчанию. Это представление для домашней страницы (<strong>index.pug</strong>) и базовый шаблон (<strong>layout.pug</strong>), который следует заменить нашим содержимым.</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="Синтаксис_шаблонов"><span class="highlight-span">Синтаксис шаблонов</span></h2> + +<p>Пример файла шаблона (ниже) демонстрирует многие наиболее полезные черты Pug.</p> + +<p>Сначала отметим, что файл отражает структуру типового HTML-файла, причем первое слов в (почти) каждой строке является элементом HTML, а отступы используются, чтобы показать вложенные элементы. Так, например, элемент <code>body</code> находится внутри элемента <code>html</code>, а элементы <code>p</code> (параграфы) - внутри элемента <code>body,</code> и так далее. Невложенные элементы (т.е. индивидуальные параграфы) располагаются в отдельных строках.</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>Атрибуты элементов определены в скобках после соответствующих элементов. В скобках располагается список пар <em>имя атрибута=значение,</em>причем элементы списка разделяются запятой или пробелом. Например:</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>Значения всех атрибутов <em>экранируются</em> (т.е. такие символы как "<code>></code>" заменяются эквивалентными кодами HTML как "<code>&gt;"</code>) , чтобы предотвратить JavaScript инъекции и межсайтовые атаки.</p> + +<p>Если после тэга стоит знак = , следующий текст рассматривается как <em>выражение</em> JavaScript. Например, шиже в первой строке, содержимое тэга <code>h1</code> будет <em>переменной </em> <code>title</code> (которая определена в файле или передана в шаблон из Express). Во второй строке содержимое параграфа - это текстовая строка, соединенная с переменной <code>title</code> . В каждом из случаев поведение по умолчанию - экранировать строки.</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>Если после тэга знак = отсутствует, тогда содержимое рассматривается как обычный текст. Внутри текста можно вставить экранированные или неэкранированные данные, применяя синтаксис <code>#{}</code> и<code> !{}</code>, как показано ниже. В простой текст можно также вставлять "сырой" HTML.</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>Совет:</strong> Почти всегда желательно экранировать данные, полученные от пользователей (при помощи синтаксиса <strong><code>#{}</code></strong> ). Данные, которым можно верить (т.е. подсчитанное количество записей, могут быть выведены без экранирования значений.</p> +</div> + +<p>Можно использовать символ конвейера ('<strong>|</strong>') в начале строки, чтобы отметить простой текст ("<a class="external external-icon" href="https://pugjs.org/language/plain-text.html" rel="noopener">plain text</a>"). Например, дополнительный текст, приведенный ниже, будет показан в той же строке, что и предыдущий, но не будет относиться к ссылке.</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 позволяет выполнять условные операции <code>if</code>, <code>else</code> , <code>else if</code> и <code>unless</code>— пример приведен ниже:</p> + +<pre class="brush: html line-numbers language-html"><code class="language-html">if title + p Переменная с именем "title" существует +else + p Переменной с именем "title" не существует</code></pre> + +<p>Можно также выполнять циклы (итерации), применяя ситаксис <code>each-in</code> или <code>while</code> . Фрагмент кода (ниже) содержит цикл по элементам массива, чтобы показать список элементов (отметим применение 'li=' для оценки "val" как переменной). Значение итератора val может быть также передано в шаблон как переменная!</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>Синтаксис разрешает также комментарии (которые попадут в результат или нет, по вашему желанию), смеси для создания повторно используемых блоков кода, операторы выбора case, и много другого. Более подробная информация - в документации <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="Расширение_шаблонов"><span class="highlight-span">Расширение шаблонов</span></h2> + +<p>Принято иметь общую структуру для всех страниц сайта, <span class="highlight-span">включая стандартную HTML-разметку для </span>заголовка, футера, навигации и т.д. Вместо того, чтобы засталять разработчиков дублировать эти образцы на каждой странице, <em>Pug</em> позволяет объявить базовай шаблон, а затем модифицировать его, заменяя только те небольшие части, которые различны на каждой конкретной странице.</p> + +<p>Например, базовый шаблон <strong>layout.pug,</strong> созданный в каркасе проекта, имеет такой вид:</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>Тэг <code>block</code> применен для отметки разделов контента, которые могут быть заменены в производных шаблона (если блок не переопределяется, будет использованиа его реализация в базовом классе).</p> + +<p>Умолчание для <strong>index.pug</strong> (созданный для каркаса проекта) показывает, как можно заменить базовый шаблон. Тэг <code>extends</code> идентифицирует базовый шаблон, который следует использовать, а затем мы используем <code>block <em>section_name,</em></code> чтобы отметить новый контент раздела, который мы заменяем.</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> |