Angular11とScullyでJamstackポートフォリオを構築する方法

提供:Dev Guides
移動先:案内検索

著者は、 Write forDOnationsプログラムの一環として寄付を受け取るために/dev /colorを選択しました。

序章

開発者として、スキル、成果、および作業を実証する方法はたくさんあります。 これらには、オープンソースの貢献、個人的なプロジェクト、スピーキングエンゲージメント、ブログ投稿などが含まれます。 ただし、作業が複数のプラットフォームに分散していて、他の人があなたを見上げたときに見つけるのが難しい場合は、これをすべて行うのは無駄になる可能性があります。 あなたの業績を紹介する中心的な場所がないと、あなたに不利に働き、潜在的な顧客、採用担当者、雇用主があなたの価値を過小評価する可能性があります。 ポートフォリオは、あなたがあなたの最高の仕事を前進させることを可能にし、あなたの成果を見つけやすくし、あなたがあなた自身をブランド化するのを助け、そして潜在的に儲かる機会につながるつながりを促進します。 ポートフォリオをブログと組み合わせると、考えを共有し、学んだことを文書化し、信頼性をさらに高める手段が得られます。

幅広い堅牢な機能を使用して、AngularおよびScullyを使用して魅力的で高速なポートフォリオを構築できます。 Angularは、ウェブからネイティブアプリやモバイルアプリまで、あらゆるものを作成できる汎用性の高いプラットフォームです。 アプリ開発を簡素化するさまざまな便利なツールを提供し、優れたパフォーマンスでより高速なアプリを実現します。

Angularアプリは、静的にすることでさらに高速化できます。 Scully を使用すると、Angularアプリをより迅速に配信できるJamstackアプリに変換できます。 Scullyは、Angularアプリケーションのすべてのルートに対して静的なHTMLページを作成する静的なサイトジェネレーターです。 これらのページは配信が速く、SEOに効果的です。 また、ポートフォリオブログを作成できるブログジェネレーターなどのツールも提供します。

このチュートリアルでは、Angular11とScullyを使用してポートフォリオとブログを作成します。 Angularアプリを生成し、プロジェクトとプロファイルを表示するページを追加し、これらのページにデータを入力するサービスを追加します。 さらに、ブログを生成して投稿を作成します。 最後に、Scullyを使用してアプリを静的サイトに変換します。

前提条件

このチュートリアルを完了するには、次のものが必要です。

  • Node.jsv12以降を実行している開発環境。 macOSまたはUbuntu18.04の場合は、Node.jsをインストールしてmacOSにローカル開発環境を作成する方法またはUbuntu18.04にNode.jsをインストールする方法の手順に従います。 その他のオペレーティングシステムについては、Node.jsダウンロードページをご覧ください。 このチュートリアルは、Node.jsv16.3.0とNPMv7.15.1を使用して作成されました。
  • Angular CLIがインストールされています。これは、AngularCLIの使用開始チュートリアルのステップ1に従って実行できます。 このチュートリアルは、AngularCLIv11.2.14を使用して作成されました。 このバージョンは、次のコマンドを実行してインストールできます:

    npm -g install @angular/[email protected]
    
  • Chromiumがインストールされています(Windowsユーザーのみ)。 ScullyにはChromiumが必要です。 macOS、Ubuntu、または別のオペレーティングシステムを使用している場合、デフォルトでは、Scullyは、静的サイトがまだインストールされていない場合に静的サイトのレンダリングに使用するローカルバージョンをダウンロードします。 ただし、Windowsを実行している場合は、ChromeをWSLにインストールする必要があります。これは、ScullyWSLの前提条件に関するこのガイドに従うことで実行できます。
  • HTMLを使用してWebサイトを構築する方法チュートリアル、 CSSを使用してHTMLをスタイル設定する方法チュートリアル、およびにあるHTML、CSS、およびTypeScriptの理解]Typescriptシリーズでコーディングする方法。
  • RxJS製品ドキュメントにあるRxJSに精通していること。
  • AngularCLI入門チュートリアルで見つけることができるAngularの理解。

ステップ1—Angularアプリをセットアップする

このステップでは、AngularCLIを使用してポートフォリオアプリを生成します。 CLIはアプリの足場を作り、アプリを実行するために必要なすべての依存関係をインストールします。 次に、Bootstrap、Font Awesome、Scullyなどの依存関係を追加します。 Scullyはアプリを静的にします。 ブートストラップは、コンポーネントとスタイリングを提供します。 FontAwesomeはアイコンを提供します。 これらの依存関係をインストールした後、ポートフォリオデータを含むフォントやJSONファイルなどのアセットを追加します。

まず、次のコマンドを実行してアプリを生成します。 portfolioと呼ばれます。 このチュートリアルにはアプリのテストが含まれていないため、-Sフラグを使用してテストファイルの生成をスキップできます。 後でテストを追加する場合は、このフラグを除外できます。

ng new portfolio -S

Angularルーティングを追加するかどうかを尋ねられたら、yesで応答します。 これは、アプリのルーティングを処理するための個別のモジュールを生成するようにCLIツールに指示します。 src/app/app-routing.module.tsで入手可能になります。

また、スタイルシート形式を選択するように求められます。 CSSを選択します。 Angularは、SCSS、Sass、Less、Stylusなどの他のスタイリングオプションを提供します。 CSSはより単純であるため、ここで使用します。

Output? Would you like to add Angular routing? Yes
? Which stylesheet format would you like to use? (Use arrow keys)
❯ CSS 

依存関係のインストール

このアプリには、 Scullyng-bootstrapFontAwesomeの3つの依存関係が必要です。 ScullyはAngularアプリを静的なアプリに変換します。 他の依存関係であるBootstrapとFontAwesomeは、ポートフォリオのルックアンドフィールをカスタマイズします。 ng-bootstrapは、Bootstrapを使用してスタイル設定されたAngularコンポーネントを提供します。 すべてのバニラブートストラップコンポーネントがAngularでそのまま動作するわけではないため、これは特に便利です。 Bootstrapは、アプリケーションに追加する必要のあるスタイリングの量も削減します。これは、Bootstrapがすでに提供しているためです。 FontAwesomeはアイコンを提供します。

Scullyを依存関係として追加すると、init回路図が実行され、静的サイトの生成に備えてアプリに変更が追加されます。 プロジェクトディレクトリから、次のコマンドを使用してScullyをプロジェクトに追加します。

ng add @scullyio/[email protected]

次に、このコマンドを使用してng-bootstrapを追加します。

ng add @ng-bootstrap/[email protected]

追加する最後の依存関係はFontAwesomeです。

npm install --save @fortawesome/[email protected]

依存関係が追加されたので、構成を追加する準備が整いました。

構成の追加

Font Awesomeをアプリで利用できるようにするには、angular.jsonファイルに縮小されたCSSへの参照を追加する必要があります。 このファイルは、プロジェクトのベースにあります。 nanoまたはお気に入りのテキストエディタを使用して、次のファイルを開きます。

nano angular.json

ファイルで、architect/buildセクションを探します。 このセクションでは、ng buildコマンドの構成について説明します。 縮小されたFontAwesomeCSSリファレンスnode_modules/@fortawesome/fontawesome-free/css/all.min.cssstyles配列に追加します。 stylesアレイはprojects/portfolio/architect/build/optionsの下にあります。 強調表示された行をファイルに追加します。

angle.json

{
  ...
  "projects": {
    "portfolio": {
      ...
      "architect": {
        "build": {
          ...
          "options": {
              ...
              "styles": [
                  "node_modules/bootstrap/dist/css/bootstrap.min.css",
                  "node_modules/@fortawesome/fontawesome-free/css/all.min.css",
                  "src/styles.css"
              ],
          }
        },
        ...
      }
    }
  }
}

これで、FontAwesomeがビルドで利用できるようになります。

ファイルを保存して閉じます。

次に、プロジェクトに新しいフォントを追加します。これは、ポートフォリオのルックアンドフィールをパーソナライズするのに役立ちます。 Google Fonts を使用して、Angularプロジェクトにフォントを追加できます。これにより、さまざまなフォントを利用できます。 linkタグを使用して、headタグで選択したフォントにリンクできます。

このチュートリアルでは、Nunitoフォントを使用します。 src/index.htmlを開き、以下で強調表示されている行を追加します。

src / index.html

... 
<head>
  <meta charset="utf-8">
  <title>Portfolio</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  <link href="https://fonts.googleapis.com/css2?family=Nunito:[email protected];400;800&display=swap" rel="stylesheet">
</head>
... 

強調表示された行は、 GoogleFontsのNunitoフォントにリンクしています。 非常に軽い、通常の、そして非常に大胆な3つの重みでそれを取得します。

ファイルを保存して閉じます。

バイオおよびプロジェクトデータの追加

次に行うことは、ポートフォリオに入れたいすべてのデータを保持するJSONファイルを作成することです。 テンプレートをデータから分離することで、アプリを改ざんすることなく、将来的に変更を加えたり、追加したりすることが容易になります。

src/assetsjsonフォルダを作成することから始めます。

mkdir src/assets/json

jsonフォルダーに、bio.jsonファイルとprojects.jsonファイルの2つのJSONファイルを作成します。 bio.jsonは、サイトに表示するプロファイルを保持します。 projects.jsonは、紹介したいプロジェクトのリストです。

bio.jsonファイルの構造は次のようになります。

src / Assets / json / bio.json

{
    "firstName": "Jane",
    "lastName": "Doe",
    "intro": [ "paragraph 1", "paragraph 2" ],
    "about": [ "paragraph 1", "paragraph 2" ]
}

introは、ホームページに表示される簡単な紹介です。 aboutは、「About」ページに表示されるより拡張されたプロファイルです。

このチュートリアルでは、サンプルのバイオを使用するか、独自のバイオをカスタマイズできます。 サンプルバイオを使用するには、bio.jsonを開き、以下を追加します。

src / Assets / json / bio.json

{
    "firstName": "Jane",
    "lastName": "Doe",
    "intro": [
        "I'm a software developer with a passion for web development. I am currently based somewhere in the world. My main focus is building fast, accessible, and beautiful websites that users enjoy.",
        "You can have a look at some of my work here."
    ],
    "about": [
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam aliquam auctor fringilla. Proin scelerisque lacinia nisl vel ultrices. Ut gravida finibus velit sit amet pulvinar. Nunc nisi arcu, pretium quis ultrices nec, volutpat sit amet nulla. Mauris semper elementum placerat. Aenean velit risus, aliquet quis lectus id, laoreet accumsan erat. Curabitur varius facilisis velit, et rutrum ligula mollis et. Sed imperdiet sit amet urna ut eleifend. Suspendisse consectetur velit nunc, at fermentum eros volutpat nec. Vivamus scelerisque nec turpis volutpat sagittis. Aenean eu sem et diam consequat euismod.",
        "Mauris dolor tellus, sagittis vel pellentesque sit amet, viverra in enim. Maecenas non lectus eget augue convallis iaculis mattis malesuada nisl. Suspendisse malesuada purus et luctus scelerisque. Cras hendrerit, eros malesuada blandit scelerisque, nulla dui gravida arcu, nec maximus nunc felis sit amet mauris. Donec lorem elit, feugiat sit amet condimentum quis, consequat id diam. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras rutrum sodales condimentum. Aenean ultrices mi vel augue dapibus mattis. Donec ut ornare nisl. Curabitur feugiat pharetra dictum."
    ]
}

編集が完了したら、ファイルを保存して閉じます。

もう1つのJSONファイルprojects.jsonは、次のような構造になります。

src / Assets / json / projects.json

[
    {
        "name": "",
        "stack": {
            "name": "Vue.js",
            "iconClasses": "fab fa-vuejs"
        },
        "description": "",
        "sourceUrl": "",
        "previewUrl": "",
        "featured": false
    }
]

各プロジェクトには、名前、説明、ソースコードがホストされている場所へのURL、およびプロジェクトがどこかにデプロイされている場合はプレビューURLがあります。 プロジェクトにプレビューURLがない場合は、省略できます。

stackオブジェクトは、プロジェクトが構築された言語またはフレームワークを示すために使用されます。 したがって、名前は言語/フレームワークの名前になり、iconClassesは言語/フレームワークのアイコンのFontAwesomeCSSクラスになります。 featuredプロパティは、プロジェクトをホームページに表示するかどうかを示します。 featuredがfalseに設定されている場合、ホームページと「プロジェクト」ページの両方ではなく、「プロジェクト」ページにのみ表示されます。

このチュートリアルでは、いくつかのサンプルプロジェクトを使用するか、独自のプロジェクトを追加できます。 サンプルプロジェクトを使用するには、projects.jsonを開き、以下を追加します。

src / Assets / json / projects.json

[
    {
        "name": "Soduko",
        "stack": {
            "name": "Angular",
            "iconClasses": "fab fa-angular"
        },
        "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
        "sourceUrl": "https://github.com",
        "previewUrl": "https://github.com",
        "featured": true
    },
    {
        "name": "E-commerce Store",
        "stack": {
            "name": "React",
            "iconClasses": "fab fa-react"
        },
        "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
        "sourceUrl": "https://github.com",
        "previewUrl": "https://github.com",
        "featured": true
    },
    {
        "name": "Algorithm Visualization App",
        "stack": {
            "name": "Angular",
            "iconClasses": "fab fa-angular"
        },
        "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
        "sourceUrl": "https://github.com",
        "previewUrl": "https://github.com",
        "featured": true
    },
    {
        "name": "Time Tracking CLI App",
        "stack": {
            "name": "Node.js",
            "iconClasses": "fab fa-node-js"
        },
        "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
        "sourceUrl": "https://github.com",
        "previewUrl": "https://github.com",
        "featured": true
    }
]

ファイルを保存して閉じます。

サーバーの起動

アプリが期待どおりに機能することを確認するには、次のコマンドを使用してAngularCLIが提供するサーバーを実行します。

ng serve

このコマンドはアプリをビルドして提供します。 変更を加えると、再構築されます。 完了すると、アプリはhttp://localhost:4200/で提供されます。

ブラウザを開き、http://localhost:4200に移動します。 次のようなプレースホルダーページが表示されます。

変更を保存するたびに、次のような出力が表示されます。

✔ Browser application bundle generation complete.

Initial Chunk Files           | Names                      |      Size
vendor.js                     | vendor                     |   3.83 MB
styles.css                    | styles                     | 202.25 kB
polyfills.js                  | polyfills                  | 141.85 kB
main.js                       | main                       |  26.08 kB
runtime.js                    | runtime                    |   9.06 kB

                              | Initial Total              |   4.20 MB

Build at:  - Hash:  - Time: 13312ms

** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **

✔ Compiled successfully.

手順を完了したら、✔ Compiled successfully.メッセージが表示されていることを確認します。 問題がある場合、ng serveコマンドはエラーを出力します。 これが発生した場合は、手順を実行して、何かを見逃したり、間違いを犯したりしていないことを確認してください。 チュートリアルを完了すると、ポートフォリオのホームページは次のようになります。

このステップでは、アプリを生成し、必要なすべての依存関係、アセット、および構成を追加しました。 また、AngularCLIによって提供されるサーバーを起動しました。 次のステップでは、コアモジュールを作成します。

ステップ2—コアモジュールを作成する

このチュートリアルでは、作成するアプリにcoreblogportfolioの3つのモジュールが含まれます。 アプリは次のように構成されます。

src / app

src/app
├── blog
├── core
└── portfolio 

ブログモジュールは、ブログのランディングページとブログ投稿ページ用です。 コアモジュールには、アプリの中心となるすべてのものが含まれています。 これらには、ヘッダーコンポーネント、データサービス、およびデータモデルが含まれます。 ポートフォリオモジュールは、「About」、「Projects」、およびホームページのすべてのポートフォリオページを保持します。

このステップでは、コアモジュールを生成します。 また、ヘッダーコンポーネント、サービス、およびデータモデルを生成してデータを入力します。 ヘッダーは各ページの上部に表示され、サイト名とメニューが含まれています。 モデルはポートフォリオデータを構成します。 サービスはポートフォリオデータをフェッチします。

コアモジュールは次のようになります。

src / app / core /

src/app/core
├── header
├── models
└── services

コアモジュールを生成するには、プロジェクトルートから次のコマンドを実行します。

ng generate module core

このコマンドは、src/app/coreフォルダー内にコアモジュールを追加し、src/app/core/core.module.tsにモジュールファイルを追加します。

コアモジュールファイルは、コアモジュールとそのインポートを定義します。 新しく生成されたモジュールファイルで、ヘッダーとサービスをサポートするためにいくつかのインポートを追加する必要があります。

core.module.tsを開き、強調表示された行を追加します(CommonModuleのインポートの後に必ずコンマを含めてください)。

src / app / core / core.module.ts

...
import { RouterModule } from '@angular/router';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { HttpClientModule } from '@angular/common/http';

@NgModule({
  declarations: [],
  imports: [
    CommonModule,
    RouterModule,
    NgbModule,
    HttpClientModule
  ]
})
...

このモジュールは、HttpClientModuleを使用して、前に作成したJSONファイルからデータをフェッチします。 また、ルーティングにNgbModuleRouterModuleng-bootstrapコンポーネントをいくつか使用します。 また、CoreModuleのインポートにそれらを追加する必要があります。

完了したら、ファイルを保存して閉じます。

データモデルの生成

このセクションでは、データモデルを生成します。 データモデルは、JSONファイルからのデータの構造を定義するために使用するインターフェイスです。 これらは、サービスおよび残りのコンポーネント全体で、戻り値およびパラメーターのタイプとして使用されます。 バイオデータの構造を定義するbioと、プロジェクトデータの構造を定義するprojectの2つのモデルが必要になります。

src / app / core / models

src/app/core/models
├── bio.ts
└── project.ts

Bioモデルはプロファイルを表し、Projectモデルは展示するプロジェクトです。 プロジェクトルートで次のコマンドを実行して、両方のモデルを生成します。

for model in bio project ; do ng generate interface "core/models/${model}"; done

このコマンドはファイルパスをループしてng generate interfaceに渡し、src/app/core/modelsフォルダーにファイルパスを作成します。

バイオモデルファイルは、バイオに含める情報を定義します。 生成されたsrc/app/core/models/bio.tsファイルに、以下で強調表示されているフィールドを追加します。

src / app / core / models / bio.ts

export interface Bio {
    firstName: string;
    lastName: string;
    about: string[];
    intro: string[];
}

このコードブロックでは、バイオモデルに名、名前、概要、紹介の各フィールドを追加しました。 最初の2つのフィールドは文字列タイプであり、最後の2つは複数の段落が含まれている可能性があるため、文字列の配列です。

ファイルを保存して閉じます。

プロジェクトファイルは、プロジェクトの構造を定義します。 ここでは、各プロジェクトで使用するフィールドを一覧表示します。 src/app/core/models/project.tsファイルで、強調表示されている行を追加します。

src / app / core / models / project.ts

export interface Project {
    name: string;
    stack: { iconClasses: string, name: string };
    description: string;
    sourceUrl: string;
    previewUrl: string;
    featured?: boolean;
}

プロジェクトモデルのフィールドを追加しました。 各プロジェクトには、名前、説明、ソースコードへのURL、およびどこかにデプロイされている場合はプレビューURLがあります。 stackオブジェクトは、プロジェクトの言語またはフレームワークを表示するために使用されます。 (名前は言語/フレームワークの名前になり、iconClassesは言語/フレームワークのアイコンのFontAwesomeCSSクラスです。)featuredプロパティは、プロジェクトをホームページに表示されます。 featuredがfalseに設定されている場合、ホームページと「プロジェクト」ページの両方ではなく、「プロジェクト」ページにのみ表示されます。

完了したら、ファイルを保存して閉じます。

このセクションでは、データのモデルを作成しました。 次に、ポートフォリオデータを取得してこれらのモデルを使用するサービスを作成します。

サービスの生成

このセクションで作成するサービスは、前に作成したJSONファイルからデータをフェッチします。 このデータをフェッチすると、コンポーネントはこれらのサービスを呼び出してデータを消費できます。 モデルは、これらのサービスの返品タイプとして使用されます。 バイオモデルはバイオサービスで使用され、プロジェクトモデルはプロジェクトサービスで使用されます。 ヘッダーおよびその他のコンポーネントのアイテムに使用するルートを決定するのに役立つ追加のヘッダーサービスを含めます。 coreモジュールには、BioServiceHeaderService、およびProjectsServiceの3つのサービスがあります。

src / app / core / services

src/app/core/services
├── bio.service.ts
├── header.service.ts
└── projects.service.ts

これらのサービスを生成するには、プロジェクトのルートディレクトリから次のコマンドを実行します。

for service in bio projects header; do ng generate service "core/services/${service}"; done

このコマンドはファイルパスをループしてng generate serviceに渡し、src/app/core/servicesフォルダーにファイルパスを作成します。

バイオサービスは、バイオJSONファイルからバイオデータをフェッチします。 これを行うには、このデータをフェッチするメソッドを追加します。 src/app/core/services/bio.service.tsファイルを開き、次の強調表示された行を追加します。

src / app / core / services / bio.service.ts

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Bio } from '../models/bio';

@Injectable({
  providedIn: 'root'
})
export class BioService {

  constructor(private http: HttpClient) { }

  getBio() {
   return this.http.get<Bio>('assets/json/bio.json');
  }
}

BioServicegetBioメソッドは、assets/json/bio.jsonファイルからバイオをフェッチします。 HttpClientサービスをコンストラクターに挿入し、それをgetBio()メソッドで使用して、ファイルに対してGETリクエストを作成します。

ファイルを保存して閉じます。

次に、HeaderServiceを変更します。 ヘッダーサービスは、現在のルートがホームページであるかどうかを確認するために使用されます。 現在のページがホームページであるかどうかを判断するメソッドを追加します。 src/app/core/services/header.service.tsファイルを開き、強調表示された行を追加します。

src / app / core / services / header.service.ts

import { Injectable } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router';
import { filter, map, startWith } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class HeaderService {

  constructor(private router: Router) { }

  isHome() {
    return this.router.events.pipe(
      filter(event => event instanceof NavigationEnd),
      map(event => {
        if (event instanceof NavigationEnd) {
          if (this.checkForHomeUrl(event.url)) {
            return true;
          }
        }

        return false;
      }),
      startWith(this.checkForHomeUrl(this.router.url))
    );
  }

  private checkForHomeUrl(url: string): boolean {
    return url.startsWith('/#') || url == '/';
  }
}

HeaderServiceでは、isHomeメソッドは、現在表示しているページがホームページであるかどうかを確認します。 これは、アンカーまでスクロールして、ホームページに注目のプロジェクトを表示する場合に便利です。

ファイルを保存して閉じます。

最後に、ProjectsServiceを変更します。 プロジェクトサービスは、プロジェクトのJSONファイルからプロジェクトデータをフェッチします。 プロジェクトデータをフェッチするメソッドを追加します。 src/app/core/services/projects.service.tsファイルを開き、内容を次のように変更します。

src / app / core / services / projects.service.ts

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { filter, mergeAll, toArray } from 'rxjs/operators';
import { Project } from '../models/project';

@Injectable({
  providedIn: 'root'
})
export class ProjectsService {

  constructor(private http: HttpClient) { }

  getProjects(featured?: boolean): Observable<Project[]> {
    let projects$ = this.http.get<Project[]>('assets/json/projects.json');

    if (featured) {
      return projects$.pipe(
        mergeAll(),
        filter(project => project.featured || false),
        toArray()
      );
    }

    return projects$;
  }
}

ProjectsServiceには、プロジェクトを取得してフィルタリングするgetProjectsメソッドがあります。 assets/json/projects.jsonファイルからプロジェクトを取得します。 HttpClientサービスをコンストラクターに挿入し、それをgetProjects()メソッドで使用して、ファイルに対してGETリクエストを作成します。 featuredパラメーターを使用すると、簡潔にするために注目のプロジェクトのみを返すように選択できます。 これは、重要なプロジェクトのみを表示したい場合にホームページで役立ちます。

ファイルを保存して閉じます。

このセクションでは、バイオおよびプロジェクトデータからバイオおよびプロジェクトデータをフェッチし、JSONファイルをプロジェクトするバイオおよびプロジェクトデータサービスを追加しました。 また、現在のページがホームページであるかどうかをチェックするヘッダーサービスも作成しました。 次のセクションでは、ポートフォリオの各ページの上部に表示されるヘッダーを作成します。 バイオサービスとヘッダーサービスを使用します。

ヘッダーの生成

ヘッダーコンポーネントは、すべてのページの上部に表示されます。 それはあなたの名前と「About」と「Projects」ページとブログへのリンクを含みます。 バイオおよびヘッダーサービスがヘッダーで使用されます。 バイオサービスは、ヘッダーにバイオデータを提供します。 ヘッダーサービスは、現在のページがホームページであるかどうかを確認するために使用され、「About」および「Projects」セクションまたはそれに基づくページへのリンクを設定します。 プロジェクトルートから次のコマンドを実行して生成します。

ng generate component core/header

ヘッダーコンポーネントは、各ページの上部に表示されます。 バイオサービスを使用して、名前と名前を取得します。 また、ヘッダーサービスを使用して、現在のページがホームページであるかどうかを判断します。 この情報を使用して、「About」および「Projects」セクションまたはページへのリンクを設定します。

header.component.tsファイルでは、バイオサービスとヘッダーサービスを挿入し、さまざまな画面サイズでのコンポーネントの応答性を処理するためのスタイリングプロパティを追加します。

header.component.tsを開き、強調表示された行を追加します。

src / app / core / header / header.component.ts

import { Component } from '@angular/core';
import { BioService } from '../services/bio.service';
import { HeaderService } from '../services/header.service';

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.css']
})
export class HeaderComponent {
  bio$ = this.bioService.getBio();
  isHome$ = this.headerService.isHome();

  menuItems = [
    { title: 'About Me', homePath: '/', fragment: 'about', pagePath: '/about' },
    { title: 'My Projects', homePath: '/', fragment: 'projects', pagePath: '/projects' },
    { title: 'My Blog', homePath: '/blog', fragment: '', pagePath: '/blog' }
  ];

  constructor(private bioService: BioService, private headerService: HeaderService) { }
}

このコンポーネントファイルでは、2つのサービスを挿入します。bioServiceはバイオJSONファイルから名前を取得し、headerServiceは現在表示されているページがホームページかどうかを判断します。 後者では、ボタンを/projectsのように別のページに移動するか、/#projectのようにアンカースクロールを実行するかを決定できます。 menuItemsには、表示するすべてのメニュー項目が含まれています。 bio$およびisHome$プロパティは、前述のサービスからのオブザーバブルを保持します。

ファイルを保存して閉じます。

次に、ヘッダーコンポーネントのテンプレートを変更します。 ここに、バイオサービスから取得したデータが表示されます。 「About」および「Projects」セクションまたはページへのリンクもここに追加されます。 src/app/core/header/header.component.htmlテンプレートファイルに、以下のコードを追加します。

src / app / core / header / header.component.html

<div class="d-flex min-vh-10 w-100 justify-content-center pb-3 pt-3 pr-4 pl-4">
    <div class="d-flex justify-content-start" *ngIf="bio$ | async as bio" routerLink="/">
        <h2 class="font-weight-bold">{{bio.firstName}}</h2>
        <h2 class="font-weight-light">{{bio.lastName}}</h2>
    </div>
    <div class="d-none d-md-flex flex-grow-1 justify-content-end align-items-start">
        <button type="button" class="ml-2 mr-2 btn btn-outline-dark border-0 font-weight-bold"
            *ngFor="let item of menuItems" [routerLink]="(isHome$ | async) ? item.homePath : item.pagePath"
            [fragment]="(isHome$ | async) ? item.fragment : ''">{{item.title}}</button>
    </div>
    <div class="d-flex d-md-none justify-content-end flex-grow-1">
        <div ngbDropdown class="d-inline-block" display="dynamic" container="body">
            <button class="btn btn-outline-dark border-0" ngbDropdownToggle>
                <i class="fas fa-lg fa-bars"></i>
            </button>
            <div ngbDropdownMenu class="dropdown-menu dropdown-menu-right">
                <button ngbDropdownItem *ngFor="let item of menuItems"
                    [routerLink]="(isHome$ | async) ? item.homePath : item.pagePath"
                    [fragment]="(isHome$ | async) ? item.fragment : ''">{{item.title}}</button>
            </div>
        </div>
    </div>
</div>

テンプレートでは、bioプロパティのデータを使用して、名前(bio.firstNameおよびbio.lastName)が表示されます。 画面のサイズに応じて、menuItemsのドロップダウンまたはボタンのリストが表示されます。 テンプレートのエイリアスパイプは、オブザーバブルからのサブスクリプション解除を処理します。 このパターンは、このチュートリアル全体を通して実行されます。

ファイルを保存して閉じます。

ヘッダーはすべてのページに表示される必要があります。 これを実現するには、いくつかの手順を実行する必要があります。 まず、CoreModuleHeaderComponentをエクスポートしてアクセス可能にする必要があります。 エクスポートするには、強調表示された行をsrc/app/core/core.module.tsに追加します。 imports配列の後にコンマを追加することを忘れないでください。

src / app / core / core.module.ts

...
@NgModule({
  ...
  
  imports: [
    ...
  ],
  exports: [ HeaderComponent ]
})
...

ヘッダーを表示するには、AppModuleにあるAppComponentテンプレートにもヘッダーを追加する必要があります。 AppModuleは、ヘッダーにアクセスするためにCoreModuleもインポートする必要があります。 これらの追加タスクは、後のステップで完了します。

このステップでは、ポートフォリオデータを整理するモデルを作成しました。 また、モデルを使用してポートフォリオデータをフェッチするサービスを作成しました。 さらに、ヘッダーアイテムに使用するルートを決定するのに役立つヘッダーサービスを作成しました。 最後に、すべてのポートフォリオページに表示されるヘッダーコンポーネントを生成しました。 次のステップでは、ポートフォリオのすべてのプライマリページを含むポートフォリオモジュールを生成します。 ポートフォリオモジュールのページでは、このセクションで作成したバイオおよびプロジェクトのサービスとモデルを使用します。

ステップ3—ポートフォリオモジュールの生成

前の手順では、ヘッダーを保持し、ポートフォリオデータのフェッチに使用するすべてのサービスとモデルを含むコアモジュールを作成しました。 このステップでは、ポートフォリオのすべての重要なページを含むポートフォリオモジュールを生成します。 これらには、ホーム、「バージョン情報」、および「プロジェクト」ページが含まれます。 このステップでは、コアモジュールで作成したサービスとモデルを使用して、これらのページを作成します。 また、各ページのルートを追加します。

ホームページには、ヘッダーとバイオサービスを使用してプロファイルの概要が表示されます。 「About」ページはより詳細なプロファイルであり、バイオサービスとモデルを使用します。 「プロジェクト」ページでは、プロジェクトサービスとモデルを使用してプロジェクトを表示し、プロジェクトを紹介します。 このステップの最後に、ポートフォリオモジュールは次のように構成されます。

src / app / Portfolio

src/app/portfolio
├── about
├── home
└── projects

最初に、ポートフォリオモジュールとポートフォリオルーティングモジュールの2つのモジュールを生成します。 ポートフォリオモジュールには、ポートフォリオのすべてのプライマリページが含まれています。 ポートフォリオルーティングモジュールは、これらのページへのルーティングを担当します。

両方のモジュールを生成するには、プロジェクトルートから次のコマンドを実行します。

ng generate module portfolio --module app --routing  --route portfolio

このコマンドは、app/portfolioフォルダーを作成し、app/portfolio/portfolio.module.tsにモジュールファイルを追加します。 このルートがapp/src/app-routing.module.tsに追加されているのがわかります。 --routingフラグは、ポートフォリオルーティングモジュールが生成されることを指定します。 このルーティングモジュールはapp/portfolio/portfolio-routing.module.tsに配置されます。

--routeフラグは、--moduleフラグで指定されているように、アプリモジュールに遅延読み込みルートを作成します。 このルートがapp/src/app-routing.module.tsに追加されているのがわかります。 また、次のセクションで説明するルーティング目的のプレースホルダーコンポーネントも追加します。

このポートフォリオモジュールは、/パスで利用できる必要があります。 これには、--route=""のように--routeフラグに空の文字列を指定する必要があります。 ただし、ng generate moduleでは、--routeフラグに空の文字列を使用できません。 したがって、プレースホルダーportfolioを使用する必要があります。 次に、このプレースホルダーをsrc/app/app-routing.module.tsの空の文字列に置き換えます。この文字列は、アプリ全体のルーティングを処理します。

src/app/app-routing.module.tsを開き、強調表示された行を置き換えます。

src / app / app-routing.module.ts

...
const routes: Routes = [
  {
    path: '',
    loadChildren: () => import('./portfolio/portfolio.module').then(m => m.PortfolioModule)
  }
];
...

これにより、ポートフォリオモジュールのすべてのページが/パスから使用できるようになります。

ファイルを保存して閉じます。

ホームページの作成

ポートフォリオモジュールを作成するコマンドは、PortfolioComponentも作成します。 これは、モジュールのルーティングを設定するときに使用されるプレースホルダーコンポーネントです。 ただし、このコンポーネントのより適切な名前はHomeComponentです。 ホームページはポートフォリオのランディングページです。 それはあなたのポートフォリオ全体の要約を持っています。 これにより、ユーザーは複数のページに移動しなくても作業の概要を簡単に把握できるようになり、興味を失うリスクが軽減されます。

このコンポーネントの名前を変更するには、最初にそれを格納するための新しいフォルダーを作成します。 プロジェクトルートから、次のコマンドを実行します。

mkdir -p  src/app/portfolio/home

次に、すべてのPortfolioComponentファイルをこの新しいフォルダーに移動します。

mv src/app/portfolio/portfolio.component.* src/app/portfolio/home/

このコマンドは、portfolio.component.*で始まる名前のすべてのファイルをsrc/app/portfolio/home/フォルダーに移動します。

次に、portfolio.component.*ファイルの名前をhome.component.*に変更します。

find src/app/portfolio/home -name 'portfolio*' -exec bash -c ' mv $0 ${0/\portfolio./home.}' {} \;

上記のコマンドを実行すると、コンポーネントの名前とパスが変更されたため、エラーが発生します。 これを修正するには、ポートフォリオルーティングモジュール、ポートフォリオモジュール、およびホームコンポーネントファイルのいくつかのファイルにいくつかの変更を加える必要があります。 これらのファイルでは、PortfolioComponentのすべてのインスタンスをHomeComponentに変更します。 また、パスを./portfolio.componentから./home/home.componentに更新します。

ポートフォリオモジュールのルーティングを処理するsrc/app/portfolio/portfolio-routing.moduleを開くことから始めます。 強調表示された変更を行います。

src / app / Portfolio / portal-routing.module

...
import { HomeComponent } from './home/home.component';

const routes: Routes = [{ path: '', component: HomeComponent }];
...

ファイルを保存して閉じます。

次に、ポートフォリオモジュールファイルであるsrc/app/portfolio/portfolio.module.tsを開きます。 強調表示された変更を行います。

src / app / portal / portal.module.ts

...
import { HomeComponent } from './home/home.component';


@NgModule({
  declarations: [
    HomeComponent
  ],
  ...
})
...

ファイルを保存して閉じます。

最後に、ホームコンポーネントファイルであるsrc/app/portfolio/home/home.component.tsを開きます。 強調表示された変更を行います。

src / app / portal / home / home.component.ts

...
@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
...
}

ファイルを保存して閉じます。

これらのファイルでは、PortfolioComponentのすべてのインスタンスをHomeComponentに変更し、HomeComponentを指すようにパスを更新しました。 これらすべてを実行すると、ポートフォリオモジュールは次のようになります。

src / app / Portfolio

src/app/portfolio
├── home
│   ├── home.component.css
│   ├── home.component.html
│   └── home.component.ts
├── portfolio-routing.module.ts
└── portfolio.module.ts

これで、ホームコンポーネントファイルへの名前とパスが更新されました。

次に、ホームコンポーネントテンプレートにコンテンツを入力してスタイルを設定します。 ホームコンポーネントはポートフォリオのメインページであり、プロファイルの概要を表示します。 (これは、上記のポートフォリオコンポーネントからホームコンポーネントに名前が変更されたコンポーネントです。)このコンポーネントでは、表示するバイオデータをフェッチし、さまざまな画面サイズでページを応答させるためのスタイルを追加する必要があります。

src/app/portfolio/home/home.component.tsを開き、次のようにコードを更新します。

src / app / portal / home / home.component.ts

import { Component } from '@angular/core';
import { BioService } from '../../core/services/bio.service';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent {
  bio$ = this.bioService.getBio();

  respOptions = [
    { viewClasses: 'd-none d-md-flex', headingClass: 'display-3', useSmallerHeadings: false },
    { viewClasses: 'd-flex d-md-none', headingClass: '', useSmallerHeadings: true }
  ];

  constructor(private bioService: BioService) { }
}

ホームページには、あなたの名前と、ここに挿入したBioServiceから取得した短い略歴が表示されます。 getBioメソッドを呼び出すと、結果のobservableがbio$プロパティに保存されます。 respOptionsプロパティは、ビューが応答することを保証するのに役立つ構成を格納します。

ファイルを保存して閉じます。

次に、ホームコンポーネントのテンプレートを変更します。 それに応じて異なる画面サイズにわたってバイオサービスからの情報を表示する責任があります。 名前、簡単なイントロ、および後で説明するaboutコンポーネントとprojectsコンポーネントを追加します。

src/app/portfolio/home/home.component.htmlを開き、次のコードを追加します。

src / app / portal / home / home.component.html

<div class="d-flex flex-column justify-content-center align-items-center w-100" *ngIf="bio$ | async as bio">
    <div class="d-flex flex-column min-vh-95 justify-content-center align-items-center w-100">
        <div *ngFor="let options of respOptions" [ngClass]="options.viewClasses"
            class="flex-column justify-content-center align-items-start w-75">
            <h1 [ngClass]="options.headingClass" class="text-left">Hello, 👋. My name is <span
                    class="font-weight-bold">{{bio.firstName+'
                    '+bio.lastName}}.</span></h1>
            <div *ngFor="let par of bio.intro">
                <h2 class="text-left" *ngIf="!options.useSmallerHeadings">{{par}}</h2>
                <h5 class="text-left" *ngIf="options.useSmallerHeadings">{{par}}</h5>
            </div>
            <button class="mt-3 mb-5 btn btn-outline-dark" routerLink="/" fragment="projects">
                See My Work
                <i class="ml-1 fas fa-angle-right"></i>
            </button>
        </div>
    </div>

    <div class="d-none d-md-block mt-5"></div>
    <app-about id="about" class="mb-3"></app-about>

    <div class="d-none d-md-block mt-5"></div>
    <app-projects id="projects" class="mb-5"></app-projects>
</div>

このテンプレートでは、bio.firstName++bio.lastNameの名前に加えて、bioからの紹介bio.introを表示します。 また、次のステップで生成するaboutコンポーネントapp-aboutとプロジェクトコンポーネントapp-projectsも表示しています。

注:このテンプレートに追加された、まだ存在しないコンポーネントが他にもいくつかあります。 これらは、AboutコンポーネントとProjectsコンポーネントです。 これらは次に追加するものです。 サーバーを実行している場合、これによりエラーが生成されます。 これらの行は、生成するまでコメントアウトできます。

src / app / portal / home / home.component.html

...
<app-about id="about" class="mb-3"></app-about>

...

<app-projects id="projects" class="mb-5"></app-projects>
...

次に、ホームコンポーネントのスタイルを追加できます。 src/app/portfolio/home/home.component.cssを開き、次の行を追加します。

src / app / portal / home / home.component.css

.min-vh-95 {
    height: 95vh;
}

ここでは、ホームページのメインコンテンツとブラウザウィンドウの端の間にスペースができるように、ホームページにスタイルを追加しています。

完了すると、ホームページは次のようになります(最後のステップでサイトをプレビューできます)。

このステップでは、ポートフォリオの要約を表示するホームページを作成しました。 次のセクションでは、「About」および「Project」コンポーネントを生成します。 これらはホームページに表示され、スタンドアロンページとしても使用されます。

アバウトページとプロジェクトページの生成

すべてのページを個別に生成する代わりに、1つのコマンドを実行して、残りの「プロジェクト」ページと「バージョン情報」ページを一度に作成できます。 これを行うには、プロジェクトルートから次のコマンドを実行します。

for page in about projects; do ng generate component "portfolio/${page}"; done

このコマンドは、各ページ名をループして生成します。

アバウトページへの入力

「About」ページには、あなたのより詳細なプロフィールが表示されます。 このページの情報はバイオサービスから取得され、バイオモデルも使用します。 このコンポーネントはホームページに表示されます。 また、独自のルートを持つスタンドアロンページになります。

「About」ページにバイオを入力するには、「About」コンポーネントファイルを変更してバイオサービスを使用します。 また、さまざまなディスプレイでページを応答させるためのオプションを設定します。 src/app/portfolio/about/about.component.tsを開き、強調表示された行を追加します。

src / app / Portfolio / about / about.component.ts

import { Component } from '@angular/core';
import { BioService } from '../../core/services/bio.service';

@Component({
  selector: 'app-about',
  templateUrl: './about.component.html',
  styleUrls: ['./about.component.css']
})
export class AboutComponent {
  bio$ = this.bioService.getBio();

  respOptions = [
    { viewClasses: 'd-none d-md-flex', headingClass: 'display-3', useSmallerHeadings: false },
    { viewClasses: 'd-flex d-md-none', headingClass: '', useSmallerHeadings: true }
  ];

  constructor(private bioService: BioService) { }
}

「About」情報はBioServiceから取得され、getBioメソッドが呼び出されると、observableはbio$プロパティに格納されます。 respOptionsは、さまざまな表示サイズにオプションのCSSクラスを提供することにより、応答性を向上させます。

ファイルを保存して閉じます。

次に、「About」ページのテンプレートを変更して、バイオサービスから取得した情報を表示できるようにします。 src/app/portfolio/about/about.component.htmlを開き、次の行を追加します。

src / app / portal / about / about.component.html

<div class="d-flex justify-content-center vw-90 mx-auto" *ngIf="bio$ | async as bio">
    <div *ngFor="let options of respOptions" [ngClass]="options.viewClasses"
        class="flex-column align-items-center text-center w-75">
        <h1 [ngClass]="options.headingClass" class="mb-5"><span class="font-weight-bold">About</span> Me</h1>
        <div *ngFor="let par of bio.about">
            <h4 *ngIf="!options.useSmallerHeadings" class="mb-4">{{par}}</h4>
            <h5 *ngIf="options.useSmallerHeadings" class="mb-4">{{par}}</h5>
        </div>
    </div>
</div>

このテンプレートでは、bio$オブザーバブルからのデータを表示します。 情報の「About」セクションをループして、「About」ページに段落として追加します。

ファイルを保存して閉じます。

完了すると、「About」ページは次のようになります(最後のステップでサイトをプレビューできます)。

プロジェクトページへの入力

「プロジェクト」ページには、プロジェクトサービスから取得されたすべてのプロジェクトが表示されます。 このコンポーネントはホームページで使用され、スタンドアロンページにもなります。 「About」コンポーネントと一緒にホームページに表示されます。 このコンポーネントをホームページで使用する場合、注目のプロジェクトのみが表示されます。 ホームページにのみ表示されるSee More Projectsボタンがあります。 クリックすると、プロジェクトの全リストページにリダイレクトされます。

「プロジェクト」ページにデータを入力するには、そのコンポーネントファイルを変更して、プロジェクトサービスからプロジェクトを取得します。 また、ヘッダーサービスを使用して、すべてのプロジェクトを表示するか、強調表示されたプロジェクトを表示するかを決定します。 また、さまざまな画面サイズでページを応答させるためのオプションを追加します。 src/app/portfolio/projects/projects.component.tsを開き、強調表示された行を追加します。

src / app / portal / projects / projects.component.ts

import { Component } from '@angular/core';
import { mergeMap } from 'rxjs/operators';
import { HeaderService } from '../../core/services/header.service';
import { ProjectsService } from '../../core/services/projects.service';

@Component({
  selector: 'app-projects',
  templateUrl: './projects.component.html',
  styleUrls: ['./projects.component.css']
})
export class ProjectsComponent {
  isHome$ = this.headerService.isHome();
  projects$ = this.isHome$.pipe(
    mergeMap(atHome => this.projectsService.getProjects(atHome))
  );

  respOptions = [
    { viewClasses: 'd-none d-md-flex', displayInColumn: false, useSmallerHeadings: false, titleClasses: 'display-3' },
    { viewClasses: 'd-flex d-md-none', displayInColumn: true, useSmallerHeadings: true, titleClasses: '' }
  ];

  constructor(private projectsService: ProjectsService, private headerService: HeaderService) { }
}

プロジェクトはProjectsServiceから来ています。 HeaderServiceを使用して、現在のページがホームページであるかどうかを判断します。 isHome$の値を使用して、プロジェクトの完全なリストを取得するか、注目のプロジェクトのみを取得するかを決定します。

ファイルを保存して閉じます。

次に、プロジェクトコンポーネントのテンプレートを変更します。 プロジェクトサービスから取得したプロジェクトを使用して、ループしてここに追加します。 カードに各プロジェクトに関する基本情報を表示し、コードがホストされている場所とプレビューできる場所へのリンクを追加します。

src/app/portfolio/projects/projects.component.htmlを開き、次の行を追加します。

src / app / portal / projects / projects.component.html

<div *ngFor="let options of respOptions" [ngClass]="options.viewClasses"
    class="flex-column align-items-center text-center vw-90 mx-auto">
    <h1 [ngClass]="options.titleClasses" class="mb-5"><span class="font-weight-bold">My</span> Projects</h1>
    <div class="d-flex vw-90"
        [ngClass]="{'justify-content-center flex-wrap': !options.displayInColumn, 'flex-column  align-items-center': options.displayInColumn}"
        *ngIf="projects$ | async as projects">
        <div *ngFor="let project of projects" class="card project-card m-3"
            [ngClass]="{'m-3': !options.displayInColumn, 'mb-3': options.displayInColumn}">
            <div class="card-body d-flex flex-column">
                <h5 class="card-title font-weight-bold text-left project-title" [title]="project.name">
                    {{project.name}}
                </h5>
                <h6 class="card-subtitle mb-2 font-weight-lighter text-left">
                    <i [ngClass]="project.stack.iconClasses"></i>
                    {{project.stack.name}}
                </h6>
                <p class="card-text text-left">
                    {{project.description}}
                </p>
                <div class="d-flex flex-row justify-content-start">
                    <a [href]="project.previewUrl" *ngIf="project.previewUrl" class="btn btn-dark mr-2">
                        <i class="fa-lg mr-1 far fa-eye"></i>
                        Preview
                    </a>
                    <a [href]="project.sourceUrl" *ngIf="project.sourceUrl" class="btn btn-dark">
                        <i class="fa-lg mr-1 fab fa-github-alt"></i>
                        Source
                    </a>
                </div>
            </div>
        </div>
    </div>
    <button *ngIf="isHome$ | async" routerLink="/projects" class="mt-3 btn btn-dark">
        See More Projects
        <i class="ml-1 fas fa-angle-right"></i>
    </button>
</div>

ここでは、projects$の各projectをカードに追加します。 カードには、プロジェクト名(project.name)、プロジェクトで使用されているテクノロジスタック(project.stack)、およびプロジェクトの機能の簡単な説明(project.description)が表示されます。 。 また、プロジェクトのコードがホストされている場所へのリンクを追加します。 さらに、プロジェクトがデプロイされている場合にプロジェクトをプレビューできる場所へのリンクを追加します。 最後に、ホームページにのみ表示されるSee More Projectsボタンがあります。 ホームページには、注目のプロジェクトのみが表示されます。 このボタンをクリックすると、ユーザーはプロジェクトの完全なリストにルーティングされます。

ファイルを保存して閉じます。

次に、プロジェクトテンプレートを変更して、プロジェクトカードのスタイルを設定します。 src/app/portfolio/projects/projects.component.cssを開き、次の行を追加します。

src / app / portal / projects / projects.component.css

.vw-20 {
    width: 20vw;
}

.project-card {
    width: 290px;
    height: 250px;
}

.project-title {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 20ch;
}

ここでは、プロジェクトカードのサイズとプロジェクトタイトルを設定します。これらは少し長くなる傾向があります。

完了すると、完全なリストの「プロジェクト」ページは次のようになります(最後のステップでサイトをプレビューできます)。

残りのポートフォリオルートの追加

各ページにアクセスできるようにするには、各ページのルートを作成する必要があります。 これらは、PortfolioModuleのルーティングを処理するPortfolioRoutingModuleに追加します。 「About」ページは/aboutで、「Projects」ページは/projectsで利用できるはずです。

ポートフォリオモジュールページのルートを作成するには、ルーティングを担当するポートフォリオルーティングモジュールファイルを変更します。 src/app/portfolio/portfolio-routing.module.tsを開き、強調表示された行を追加します。

src / app / Portfolio / portal-routing.module.ts

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { ProjectsComponent } from './projects/projects.component';
import { AboutComponent } from './about/about.component';

const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'about', component: AboutComponent },
  { path: 'projects', component: ProjectsComponent }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class PortfolioRoutingModule { }

ここでは、コンポーネントへのパスを指定してroutes配列に追加することにより、「About」ページと「Projects」ページにルートを追加しました。

このステップでは、3つのページをそれぞれ作成し、それらのルートを追加して、ポートフォリオモジュールを完了しました。 次のステップでは、ブログコンポーネントを生成します。

ステップ4—ブログモジュールの生成

このステップでは、ブログのランディングページと投稿ページを含むブログモジュールを生成します。 ブログを最初から作成する代わりに、Scully回路図を使用して、機能するブログに必要なすべてを設定します。 Scully回路図はモジュールを生成し、ブログへのルーティングを処理するルーティングモジュールを追加し、ブログ投稿を表示するブログコンポーネントを作成します。 ブログコンポーネントは、マークダウンファイルに書き込んだ投稿を表示します。 後のステップで新しいブログ投稿を作成するときに、これらのマークダウンファイルがどこにあるかがわかります。 ブログをレンダリングするとき、Scullyは、作成したブログ投稿のマークダウンバージョンを取得し、静的なHTMLページに変換します。これにより、読者への配信が高速になります。

プロジェクトルートから次のコマンドを実行することで、アプリのブログサポートを有効にし、モジュールを生成できます。

ng generate @scullyio/init:blog

上記のコマンドは、src/app/blogにブログモジュールを作成し、ブログマークダウンファイルが存在するプロジェクトのベースにblogフォルダーを作成し、AppRoutingModuleにモジュールの遅延読み込みルートを追加します]、モジュールのベースにブログコンポーネントを作成します。

次に、ブログコンポーネントが存在するモジュール内にフォルダーを作成します。

mkdir src/app/blog/blog

ブログコンポーネントをこのフォルダーに移動するには、次のコマンドを実行します。

mv src/app/blog/blog.component.* src/app/blog/blog/

これにより、次のブログモジュール構造になります。

src / app / blog

src/app/blog
├── blog
│   ├── blog.component.css
│   ├── blog.component.html
│   ├── blog.component.spec.ts
│   └── blog.component.ts
├── blog-routing.module.ts
└── blog.module.ts

このモジュールは再構築されているため、一部のパスが壊れて更新が必要になります。 blog-routing.module.tsblog.module.tsの2つのファイルは、BlogComponentへの新しいパスで更新する必要があります。

blog-routing.module.tsを開き、次のようにインポートを更新します。

src / app / blog / blog-routing.module.ts

...
import { BlogComponent } from './blog/blog.component';
...

ファイルを保存して閉じます。

次に、blog.module.tsを開き、次のようにインポートを更新します。

src / app / blog / blog.module.ts

...
import { BlogComponent } from './blog/blog.component';
...

ファイルを保存して閉じます。

次に、ブログコンポーネントのテンプレートを変更します。 ブログコンポーネントの役割は、ブログ投稿を表示することです。 このコンポーネントは、Scullyブログの回路図面に既に入力されているため、最小限の編集で済みます。 ブログ投稿のコンテンツを保持するコンテナーにスタイルを追加します。 src/app/blog/blog/blog.component.htmlを開き、ボイラープレートの内容を次の行に置き換えます。

src / app / blog / blog / blog.component.html

<div class="vw-70">
    <scully-content></scully-content>
</div> 

テンプレートに追加されたスタイルにより、ブログコンポーネントのページ内の間隔が広がります。 <scully-content></scully-content>は、マークダウンブログコンテンツをレンダリングします。

ファイルを保存して閉じます。

次に、見出しを中央に配置してスタイルを変更します。これにより、ブログコンポーネントのルックアンドフィールが向上します。 src/app/blog/blog/blog.component.cssを開き、コンテンツを次の行に置き換えます。

src / app / blog / blog / blog.component.css

h1, h2, h3, h4, h5, h6 {
  text-align: center;
  padding: 1rem;
}

ファイルを保存して閉じます。

完了すると、ブログは次のようになります(最後のステップでサイトをプレビューできるようになります)。

ブログランディングページの生成

ブログモジュールを作成し、ブログ投稿にスタイルを追加したので、ブログランディングページを生成し、ランディングページにスタイルを追加します。

ブログのランディングページには、すべてのブログ投稿が一覧表示されます。 プロジェクトルートで次のコマンドを実行することにより、これを生成できます。

ng generate component blog/blog-landing

これにより、次の構造になります。

src / app / blog

src/app/blog
├── blog
│   ├── blog.component.css
│   ├── blog.component.html
│   ├── blog.component.spec.ts
│   └── blog.component.ts
├── blog-landing
│   ├── blog-landing.component.css
│   ├── blog-landing.component.html
│   └── blog-landing.component.ts
├── blog-routing.module.ts
└── blog.module.ts

次に、ブログのランディングページのコンポーネントファイルを変更して、すべてのブログ投稿を一覧表示します。 ここでは、ルートに/blog/が含まれるすべてのページを取得し、リストに表示します。 また、さまざまな画面サイズでページを応答させるためのオプションを追加します。

src/app/blog/blog-landing/blog-landing.component.tsを開き、次の変更を加えます。

src / app / blog / blog-landing / blog-landing.component.ts

import { Component } from '@angular/core';
import { ScullyRoute, ScullyRoutesService } from '@scullyio/ng-lib';
import { map } from 'rxjs/operators';

@Component({
  selector: 'app-blog-landing',
  templateUrl: './blog-landing.component.html',
  styleUrls: ['./blog-landing.component.css']
})
export class BlogLandingComponent {
  links$ = this.scully.available$.pipe(
    map(routes => routes.filter((route: ScullyRoute) => route.route.startsWith('/blog/')))
  );

  respOptions = [
    { viewClasses: 'd-none d-md-flex', displayInColumn: false, titleClasses: 'display-3' },
    { viewClasses: 'd-flex d-md-none', displayInColumn: true, titleClasses: '' }
  ];

  constructor(private scully: ScullyRoutesService) { }
}

すべてのブログルートのリストを取得するには、ScullyRoutesServiceを使用します。 available$ observableは、Scullyによってレンダリングされ、publishedとしてマークされたすべてのルートを返します。 ブログ投稿が公開されているかどうかは、マークダウンファイルのフロントマターでマークできます。 (これについては次のステップで説明します。)このオブザーバブルは、ポートフォリオからのルートを含むすべてのルートを返します。 したがって、プレフィックス/blog/を含むルートのみをフィルタリングします。 ブログルートはlinks$プロパティによって保持されます。 respOptionsプロパティは、応答性に役立ちます。

ファイルを保存して閉じます。

次に、ブログのランディングページのテンプレートを変更して、利用可能なすべてのブログ投稿をカードに一覧表示し、それらにリンクします。 ブログのタイトルも含まれています。 src/app/blog/blog-landing/blog-landing.component.htmlを開き、次の行を追加します。

src / app / blog / blog-landing / blog-landing.component.html

<div *ngFor="let options of respOptions" [ngClass]="options.viewClasses"
    class="flex-column align-items-center text-center vw-90 mx-auto">
    <h1 [ngClass]="options.titleClasses" class="mb-5"><span class="font-weight-bold">Jane's</span> Blog</h1>
    <div [ngClass]="{'justify-content-center flex-wrap': !options.displayInColumn,  'flex-column align-items-center': options.displayInColumn}"
        class="d-flex vw-90">
        <div *ngFor="let page of links$ | async" class="card post-card m-3">
            <div class="card-img-top bg-dark">
                <i class="far fa-newspaper fa-4x m-5 text-white"></i>
            </div>
            <div class="card-body d-flex flex-column">
                <h5 class="card-title post-title" [title]="page.title">{{page.title}}</h5>
                <p class="card-text post-description flex-grow-1">{{page.description}}</p>
                <a [routerLink]="page.route" class="btn btn-outline-dark align-self-center">
                    <i class="fa-lg mr-1 far fa-eye"></i>
                    Read
                </a>
            </div>
        </div>
    </div>
</div>

このテンプレートでは、Scullyルーターサービスから返されたすべてのブログ投稿をループします。 ブログ投稿ごとに、カードを追加します。 各カードには、ブログ投稿のタイトルと説明が表示されます。 クリックしてブログ投稿に移動できるリンクも追加されています。

ファイルを保存して閉じます。

最後に、ブログのランディングテンプレートにスタイルを追加します。 ページに追加されたプロジェクトカードのスタイルを設定します。 src/app/blog/blog-landing/blog-landing.component.cssを開き、次の行を追加します。

src / app / blog / blog-landing / blog-landing.component.css

.post-card {
    width: 290px;
    height: 360px;
}

.post-title {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 20ch;
}

ファイルを保存して閉じます。

完了すると(そしてブログ投稿を追加した後)、ブログのランディングページは次のようになります(最後のステップでサイトをプレビューできるようになります)。

ブログランディングルートの追加

/blogパスでブログのランディングページにアクセスできるようにするには、BlogRoutingModuleにそのルートを追加する必要があります。 これを追加しないと、アプリで利用できなくなります。 src/app/blog/blog-routing.module.tsを開き、強調表示された行を追加します。

src / app / blog / blog-routing.module.ts

...
import { BlogLandingComponent } from './blog-landing/blog-landing.component';

const routes: Routes = [
  { path: '', component: BlogLandingComponent },
  { path: ':slug', component: BlogComponent },
  { path: '**', component: BlogComponent }
];
...

ここでは、BlogLandingComponentのルートをroutesアレイに追加しました。 これにより、/blogルートでアクセスできるようになります。

ファイルを保存して閉じます。

このステップでは、ブログ投稿ページとブログランディングページの2つのページを含むブログモジュールを作成しました。 これらのページにスタイルを追加し、ブログのランディングルートを追加して、ランディングページが/blogパスでアクセスできるようにしました。 次のステップでは、新しいブログ投稿を追加します。

ステップ5—新しいブログ投稿を追加する

このステップでは、Scullyを使用して、ブログのランディングページに表示される新しいブログ投稿を生成します。 Scullyを使用すると、ブログ投稿として機能するマークダウンファイルを生成できます。 前の手順で生成したブログコンポーネントは、ブログ投稿のマークダウンバージョンを読み取り、それを表示します。 Markdownを使用すると、リッチフォーマットのブログコンテンツをすばやく簡単に作成できます。 Scullyはこれらのファイルを作成するだけでなく、それらを格納するフォルダーを追加します。 また、各投稿にタイトルや説明などのメタデータを追加します。 一部のメタデータは、投稿の表示方法を決定するために使用されます。 後で、Scullyを使用して、これらのマークダウンブログ投稿の静的HTMLページバージョンを生成します。

投稿する前に、名前を思いつく必要があります。 このチュートリアルでは、「ブログ投稿1」というタイトルの投稿を作成します。 プロジェクトルートの--nameフラグを使用して、以下のコマンドにこの名前を指定します。

ng generate @scullyio/init:post --name="Blog Post 1"

出力は次のようになります。

Output? What's the target folder for this post? blog
    ✅️ Blog ./blog/blog-post-1.md file created
CREATE blog/blog-post-1.md (103 bytes)

これにより、プロジェクトルートに/blog/blog-post-1.mdファイルが作成されます。 ファイルの内容は次のようになります。

blog / blog-post-1.md

---
title: Blog Post 1
description: blog description
published: false
---

# Blog Post 1

ブログ投稿にコンテンツを追加して満足したら、publishedtrueに変更すると、サイトをレンダリングしたときにブログのランディングページに表示されます。 まだ公開されていない投稿を表示するには、slugプロパティを使用できます。

たとえば、次のスラッグを追加したとします。

blog / blog-post-1.md

---
title: Blog Post 1
description: blog description
published: true
slug: alternate-url-for-blog-post-1
---

# Blog Post 1

サーバーを実行すると、https://localhost:1668/blog/alternate-url-for-blog-post-1でこの投稿を表示できます。 ただし、この未公開の投稿は、published: trueとマークされていない限り、ブログのランディングページに表示されません。 後のステップでわかるように、Scullyルートを生成するとき、Scullyはすべての未公開の投稿にスラッグを追加するので、追加する必要はありません。

投稿にコンテンツを追加するには、タイトルの後に始めます。 すべての投稿コンテンツはマークダウンする必要があります。 生成したマークダウン投稿で使用できるコンテンツの例を次に示します。

/blog/blog-post-1.md

---
title: Blog Post 1
description: Your first blog post
published: true
---

# Blog Post 1

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus vitae tempor erat, eget accumsan lorem. Ut id sem id massa mattis dictum ullamcorper vitae massa. In luctus neque lectus, quis dictum tortor elementum sit amet. Mauris non lacinia nisl. Nulla tristique arcu quam, quis posuere diam elementum nec. Curabitur in mi ut purus bibendum interdum ut sit amet orci. Duis aliquam tristique auctor. Suspendisse magna magna, pellentesque vitae aliquet ac, sollicitudin faucibus est. Integer semper finibus leo, eget placerat enim auctor quis. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed aliquam nibh in mi convallis mattis nec ac mi. Nam sed sagittis purus.

完了したら、ファイルを保存して閉じます。

次のコマンドを実行して、他の投稿を生成できます。

ng generate @scullyio/init:post --name="Blog Post 2"
ng generate @scullyio/init:post --name="Blog Post 3"

これらのコマンドは、/blog/フォルダーに割り当てた名前で他の2つのマークダウンファイルを作成します。 最初の投稿で行ったように、生成されたファイルに上記のサンプルコンテンツを入力できます。

このステップでは、最初のScullyブログ投稿を作成しました。 次の手順では、アプリを完成させるために必要な変更について説明します。

ステップ6—アンカースクロールの有効化とアプリコンポーネントテンプレートのクリーンアップ

アプリをプレビューする前に最後に行うことは、アンカースクロールを有効にし、グローバルスタイルを追加し、app.component.htmlをクリーンアップすることです。

ホームページで、訪問者がヘッダー内のアイテムをクリックすると、同じページの特定のセクションに移動する必要があります。 これを実現するには、Angularアプリでアンカースクロールを有効にする必要があります。 これらすべての変更を行うと、ホームページのセクションにスクロールできるようになります。

まず、アプリルーティングモジュールのモジュールファイルを変更します。 このモジュールは、アプリ全体のルーティングを担当します。 ここでは、アンカーのスクロールを有効にします。 src/app/app-routing.module.tsを開き、強調表示された部分を追加します。

src / app / app-routing.module.ts

...
@NgModule({
  imports: [RouterModule.forRoot(routes, { anchorScrolling: 'enabled' })],
  exports: [RouterModule]
})
...

{ anchorScrolling: 'enabled' }を追加すると、ルーターモジュールでanchorScrollingが有効になるため、ホームページのさまざまなセクションにジャンプできます。

ファイルを保存して閉じます。

Angularアプリを生成すると、メインアプリコンポーネント(src/app/app.component.html)のテンプレートにプレースホルダーコンテンツが含まれます。 このプレースホルダーのコンテンツは、ポートフォリオのすべてのページに表示されます。 これは次のようになります。

ポートフォリオにこのプレースホルダーコンテンツは必要ないため、これを削除します。

生成されたプレースホルダーコードをメインページから削除するには、src/app/app.component.htmlを開き、その内容を次の行に置き換えます。

src / app / app.component.html

<div class="d-flex flex-column h-100 w-100">
    <app-header></app-header>
    <div class="d-flex flex-column flex-grow-1 align-items-center justify-content-center">
        <router-outlet></router-outlet>
    </div>
</div>

このファイルでは、ヘッダーコンポーネントであるapp-headerを追加し、router-outletの周囲にコンテナーdivを配置して、ルーティングされたページがその下に表示されるようにします。

次に、AppModuleapp-headerにアクセスできることを確認する必要があります。 app-headerは別のモジュールに存在するため、App Moduleは現在そのモジュールにアクセスできません。 CoreModuleはヘッダーコンポーネントへのアクセスを提供するため、src/app/app.module.tsへのインポートとしてCoreModuleを追加する必要があります。 app.module.tsを開き、以下で強調表示されているようにインポートを追加します。

src / app / app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { ScullyLibModule } from '@scullyio/ng-lib';
import { CoreModule } from './core/core.module';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    ScullyLibModule,
    CoreModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

この変更を行うと、AppModuleapp-headerにアクセスできるようになります。

最後に、src/styles.cssを変更して、アプリのグローバルスタイルを調整します。 アプリ全体のいくつかのコンポーネントは、このファイルのスタイルを使用します。 これは、アプリの全体的なルックアンドフィールに貢献し、スタイリングがコンポーネント間で再利用されるため、繰り返しを防ぎます。

サイトの運営に進む前に、src/styles.cssを開き、次の行を追加します。

src / styles.css

html, body {
    width: 100%;
    height: 100%;
}

body {
    font-family: 'Nunito', Arial, Verdana, Geneva, Tahoma, sans-serif;
    background: white;
    background-image: radial-gradient(lightgray 5.5%, transparent 0);
    background-size: 30px 30px;
}

.vw-90 {
    width: 90vw;
}

.vw-80 {
    width: 80vw;
}

.vw-70 {
    width: 80vw;
}

.min-vh-10 {
    min-height: 10vh;
}

このファイルでは、htmlbodyがページ全体の高さと幅をとることを確認します。 また、Nunitoをデフォルトのフォントにし、幅と高さを設定するためのさまざまなスタイルクラスを含めます。

この手順では、アンカースクロールを有効にし、グローバルスタイルを追加し、アプリコンポーネントテンプレートをクリーンアップしました。 次のステップでは、サイトを構築し、Scullyルートをレンダリングして、静的ポートフォリオを提供します。

ステップ7—静的サイトのプレビュー

必要なコード変更がすべて完了したので、Scullyを使用してポートフォリオをプレビューできます。 これには、サイトの構築、Scullyルートの生成、静的バージョンのサイトの提供が含まれます。 このステップでは、ScullyはAngularアプリを静的サイトに事前レンダリングし、Angularアプリと静的ポートフォリオの両方にサービスを提供するサーバーを提供します。

Scullyがポートフォリオを事前にレンダリングする前に、ポートフォリオを構築する必要があります。

ng build

このコマンドは、ポートフォリオをdist/portfolioにコンパイルします。

出力は次のようになります。

Compiling @angular/core : es2015 as esm2015
Compiling @angular/common : es2015 as esm2015
Compiling @angular/platform-browser : es2015 as esm2015
Compiling @angular/router : es2015 as esm2015
Compiling @angular/platform-browser-dynamic : es2015 as esm2015
Compiling @angular/common/http : es2015 as esm2015
Compiling @angular/forms : es2015 as esm2015
Compiling @scullyio/ng-lib : es2015 as esm2015
Compiling @ng-bootstrap/ng-bootstrap : es2015 as esm2015
✔ Browser application bundle generation complete.
✔ Copying assets complete.
✔ Index html generation complete.

Initial Chunk Files           | Names                      |      Size
vendor.js                     | vendor                     |   3.49 MB
styles.css                    | styles                     | 202.25 kB
polyfills.js                  | polyfills                  | 141.85 kB
main.js                       | main                       |  24.91 kB
runtime.js                    | runtime                    |   9.06 kB

                              | Initial Total              |   3.86 MB

Lazy Chunk Files              | Names                      |      Size
portfolio-portfolio-module.js | portfolio-portfolio-module |  34.19 kB
blog-blog-module.js           | blog-blog-module           |  15.28 kB

Build at:  - Hash:  - Time: 29012ms

ビルドが完了したら、次を実行します。

npx scully

Scullyは、ルートごとに個別のindex.htmlを作成することにより、ポートフォリオ全体を事前にレンダリングします。 事前にレンダリングされたポートフォリオは、dist/staticに配置されます。 このフォルダはこれに似ているはずです。 (わかりやすくするために、一部のファイルは削除されています。)

距離/静的

dist/static
├── about
│   └── index.html
├── assets
├── blog
│   ├── angular-unit-testing
│   │   └── index.html
│   ├── create-a-blog-using-vue.js
│   │   └── index.html
│   ├── how-to-create-a-twitter-bot
│   │   └── index.html
│   └── index.html
├── index.html
└── projects
    └── index.html

各ルートに独自のindex.htmlファイルがあることに注意してください。

静的サイトをプレビューするには、次のコマンドを実行します。

npm run scully:serve

このコマンドは、http://localhost:1668/で静的Scullyサーバーを起動し、静的ポートフォリオを提供します。 (サイトのプレビューが完了したら、サーバーが実行されているターミナルでCtrl + Cを使用してサーバーを強制終了できます。)

注:ScullyはPuppeteerの検索で問題が発生する可能性があります。 これは、CIサービスやクラウド内の仮想マシンなどの制限された環境でアプリを実行しようとしたときに発生します。 DigitalOcean Dropletでアプリを実行しようとすると、このエラーが発生する場合があります。 エラーは次のようになります。

=================================================================================================
Puppeteer cannot find or launch the browser. (by default chrome)
 Try adding 'puppeteerLaunchOptions: {executablePath: CHROMIUM_PATH}'
 to your scully.*.config.ts file.
Also, this might happen because the default timeout (60 seconds) is to short on this system
this can be fixed by adding the --serverTimeout=x cmd line option.
   (where x = the new timeout in milliseconds)
When this happens in CI/CD you can find some additional information here:
https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md
=================================================================================================

これを修正するには、強調表示された部分をscully.portfolio.config.tsに追加します。

scully.portfolio.config.ts

import { ScullyConfig } from '@scullyio/scully';
export const config: ScullyConfig = {
  projectRoot: "./src",
  projectName: "portfolio",
  outDir: './dist/static',
  routes: {
    '/blog/:slug': {
      type: 'contentFolder',
      slug: {
        folder: "./blog"
      }
    },
  },
  puppeteerLaunchOptions: {args: ['--no-sandbox', '--disable-setuid--sandbox']}
};

puppeteerLaunchOptionsオプションを使用すると、Puppeteerのデフォルトのオプションを変更して、ご使用の環境で機能するオプションで上書きすることができます。 --no-sandboxおよび--disable-setuid--sandboxは、Puppeteerに提供されている複数層のサンドボックスを無効にします。 このChromeトラブルシューティングリソースの詳細を読むことができます。 セットアップによっては、Chromiumを実行するために追加の依存関係をインストールする必要がある場合もあります。これについては、Puppeteerトラブルシューティングガイドで詳しく知ることができます。


http://localhost:4200のホームページは次のようになります。

このステップでは、Angularアプリをビルドし、静的サイトに事前にレンダリングして、Scullyを使用して提供しました。

結論

このチュートリアルでは、Angularポートフォリオアプリを生成して構成しました。 また、ポートフォリオデータを処理し、アプリの中心となるコンポーネントを保持するためのコアモジュールを作成しました。 さらに、あなたはあなたの経歴、プロジェクト、そしてプロフィールを紹介する重要なページからなるポートフォリオモジュールを作りました。 ブログのランディングページと投稿ページで作成されたブログモジュールを作成しました。 最後に、Scullyを使用してAngularポートフォリオを静的サイトに変換しました。

あなたのポートフォリオにはまだ多くのことが関係しています。 あなたが書いたあなたのスキルと記事を示すためにページを追加することができます。 連絡先ページを追加して、他の人があなたに連絡できるようにすることもできます。 スピーキングエンゲージメント、ビデオチュートリアルチャネル、ポッドキャスト、または会議トークがある場合は、それらを披露するページを作成できます。

さらに、Scullyは、ブログ投稿のコードのシンタックスハイライト統合などの他の便利な機能を提供します。 Scully製品ドキュメントで、prismjsを使用した構文の強調表示について詳しく知ることができます。

最後に、テストを追加してポートフォリオを展開できます。 このアプリのライブバージョンは、作成者のGitHubで表示できます。 このプロジェクトのソースコード(およびより高度なバージョン)はGitHubで入手できます。 このような静的サイトをDigitalOceanにデプロイする方法については、AppPlatformのこれらのチュートリアルをご覧ください。

注:このチュートリアルは、Angularメジャーバージョン11を使用して作成されました。 Scullyの最新バージョンと互換性のある最新バージョンにアップグレードすることを検討してください。 Angularの更新ツールを使用して、これを行う方法を理解できます。 Font Awesomeやng-bootstrapなど、チュートリアルで使用されている他の依存関係を更新することも検討してください。