One place for hosting & domains

      Angular

      Angular Animations: Get Started


      Web animations add dynamic graphics and effects to a web page. Movement on a web page communicates information to users and adds visual interest. Animation on a web page is typically achieved by changing the state of an HTML element over time. For example, an HTML element’s color or position can change as a result of user interaction on a web page. Most popular web frameworks offer animation support that simplifies animating elements on your web pages or applications. This guide shows you how to get started creating animations using the Angular web framework.

      Setup the Angular Project

      1. Follow the steps in our
        Getting Started with Angular
        guide to install Node.js, the Node Version Manager (nvm), and Angular. As a result of following those steps, you should have a directory named example-app in your home folder.

      2. In your Angular project’s root application module, enable the animations module by importing the BrowserAnimationsModule as shown in the following code:

        File: /home/username/example-app/src/app/app.module.ts
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        
        import { NgModule } from '@angular/core';
        import { BrowserModule } from '@angular/platform-browser';
        import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
        
        @NgModule({
          imports: [
            BrowserModule,
            BrowserAnimationsModule
          ],
          declarations: [ ],
          bootstrap: [AppComponent]
        })
        export class AppModule { }
            

        The bootstrap key bootstraps the component that contains the majority of your animation code. You create this code in the next section.

      Angular Animation Example

      The example Angular code used in this guide animates the transition between two background colors contained within a <div> HTML element.

      Add Metadata to Angular Component

      In your preferred text editor, open your Angular application’s /home/username/example-app/src/app/app.component.ts file and add the animations metadata property to the declared component. The majority of the code used to animate the <div> element is contained in the animations metadata option.

      File: /home/username/example-app/src/app/app.component.ts
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      
      @Component({
        selector: 'app-root',
        templateUrl: './app.component.html',
        styleUrls: ['./app.component.css'],
        styleUrls: ['app.component.css'],
        animations: [
            //Add the animations metadata option
        ]
      })
      ...
          

      The HTML that is animated is stored in the component’s HTML template which is the ./app.component.html file. You add the HTML code to this file in the next section.

      Note

      Ensure that the top of your app.component.ts file includes the following import statements.

      File: /home/username/example-app/src/app/app.component.ts
      1
      2
      
      import { Component } from '@angular/core';
      import { trigger, state, style, animate, transition } from '@angular/animations';

      Create the Angular Component HTML Template

      Your Angular project already contains a component HTML template file. The app.component.ts component file points to the location of the ./app.component.html file in the templateURL metadata. In this section, you add the HTML to animate with Angular.

      1. Open your Angular project’s /home/username/example-app/src/app/app.component.html file and delete all of the example boilerplate code.

      2. Add the example HTML template code as shown below:

        File: /home/username/example-app/src/app/app.component.html
        1
        2
        3
        4
        5
        6
        7
        
        <h1>Angular Animation Example</h1>
        
        <div (click)="changeState()" [@changeDivColor]=currentState >
          <h2>A Heading Contained in a Div</h2>
          <p>Click inside the Div to view the div's background color change using an Angular animation.</p>
        </div>
            

        The HTML template adds a <div> element that gets animated. For now, the <div> does not call any of the functionality that enables the <div> to be animated. You add this in the next section.

      3. Run the Angular local server with the following command:

         ng serve
        
      4. Navigate to http://localhost:4200 in a browser window and you should see the rendered HTML.

      Add an Angular Animation Trigger

      In this section, you add an Angular animation trigger. The trigger is used to bind the animation code and behavior to the HTML element that you want to animate. The animation trigger contains the animation states that define the div’s background color state change.

      Open the component file and add the trigger() function to the animations metadata object.

      File: /home/username/example-app/src/app/app.component.ts
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      
      @Component({
        selector: 'app-root',
        templateUrl: './div-animation.component.html',
        styleUrls: ['./app.component.css'],
        animations: [
          trigger('changeDivColor', [
              //Animation states are added here.
          ])
        ]
      })
      ...
          

      In the example above, changeDivColor is the animation trigger. This trigger can now be called in your Angular component template. You complete that step in one of the following sections. The next section shows you how to add animation states to your component.

      Add Angular Animation States

      Angular animation states specify the change that you want to apply during your animation. For the example, the <div> background color’s state should change from one color to another. You use Angular’s state() function to name the state and define the element’s CSS styles that are associated with the state.

      Open the component file and add the two state() functions to the changeDivColor state of the trigger() function as shown below:

      File: /home/username/example-app/src/app/app.component.ts
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      
      @Component({
        selector: 'app-root',
        templateUrl: './div-animation.component.html',
        styleUrls: ['./app.component.css'],
        animations: [
          trigger('changeDivColor', [
            state('start', style({
              border: '5px outset blue',
              backgroundColor: 'green',
              textAlign: 'center'
            })),
            state('end', style({
              border: '5px outset blue',
              backgroundColor: 'lightgreen',
              textAlign: 'center'
            })),
          ])
        ]
      })
      ...
          

      The state functions assign a name to each state and provide their associated styles. The start state uses a background color of green, while the end state uses a background color of light green.

      Add Angular Animation Transitions

      Now, you need to define the Angular animation’s transitions from one state to another. To do this, you use the transition() function.

      Open the component file and add the transition() function to the end of the trigger() function as shown below:

      File: /home/username/example-app/src/app/app.component.ts
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      
      ...
      @Component({
        selector: 'app-root',
        templateUrl: './div-animation.component.html',
        styleUrls: ['./app.component.css'],
        animations: [
          trigger('changeDivColor', [
            state('start', style({
              border: '5px outset blue',
              backgroundColor: 'green',
              textAlign: 'center'
            })),
            state('end', style({
              border: '5px outset blue',
              backgroundColor: 'lightgreen',
              textAlign: 'center'
            })),
            transition('start <=> end', animate('1000ms'))
          ])
        ]
      })
      ...
          

      The updated code above creates a state-to-state transition. The first state transition goes from the start state to the end state, while the second transition does the opposite. This state transition allows you to toggle between both states each time a user clicks on the bound HTML element (the <div> element). The transition() function uses the animate() function to specify the timing for the transition.

      Add the Toggle Function

      Now that your animation is defined, you need to add a function that can detect the <div> state and toggle between the two states.

      Add the following toggleState() function to your AppComponent class. The AppComponent class is located below the @Component decorator.

      File: /home/username/example-app/src/app/app.component.ts
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      
      ...
      export class AppComponent {
        title = 'example-app';
      
        divState = 'start';
      
        toggleState() {
          this.divState = this.divState === 'start' ? 'end' : 'start';
        }
      }
          

      The code above adds a divState variable that stores the current toggle state of the <div>. The toggleState() function uses a conditional ternary operator to update the value of divState depending on its current value.

      Now that your animation code and toggle functionality is in place, you need to update the Angular HTML template to bind everything together. You complete this in the next section.

      Bind the Animation Trigger to the HTML Component Template

      In this section, you bind the animation trigger you created in the previous steps to the <div> element.

      1. Open your HTML component template and add the animation trigger to the <div>. You also add a click event that invokes the toggleState() function you added to the AppComponent class declaration.

        File: /home/username/example-app/src/app/app.component.html
        1
        2
        3
        4
        5
        6
        7
        
        <h1>The div element</h1>
        
        <div (click)="toggleState()" [@changeDivColor]=divState>
          <h2>This is a heading in a div element</h2>
          <p>This is some text in a div element.</p>
        </div>
            

        Notice that the @changeDivColor trigger binding is set to the value of the divState variable. This enables the div to begin in the start toggle state. You are now ready to view your animation.

      2. Navigate to http://localhost:4200 in a browser window and you should see the rendered HTML.

        Angular rendered green HTML div.

        Click inside the <div> container and you should see the animated background color transition from green to light green and vice versa, with each click.

        Angular rendered light green HTML div.

      Conclusion

      This guide showed you how to use the Angular framework to get started creating animations. Angular’s
      triggers
      ,
      states
      , and
      transitions
      help you animate elements on a web page. The basic example included in this guide provides a foundation that you can use to
      create more advanced animations
      using the Angular query() and stagger() functions.

      More Information

      You may wish to consult the following resources for additional information
      on this topic. While these are provided in the hope that they will be
      useful, please note that we cannot vouch for the accuracy or timeliness of
      externally hosted materials.



      Source link

      How To Build a Jamstack Portfolio with Angular 11 and Scully


      The author selected /dev/color to receive a donation as part of the Write for DOnations program.

      Introduction

      As a developer, there are many ways you can demonstrate your skills, accomplishments, and work. These include open source contributions, personal projects, speaking engagements, blog posts, etc. However, doing all this may be in vain if your work is scattered on multiple platforms and difficult to find when people look you up. Not having a central place to showcase your achievements can work against you and may cause potential clients, recruiters, and employers to underestimate your worth. A portfolio allows you to put your best work forward, makes your accomplishments easy to find, helps you brand yourself, and facilitates connections that lead to potentially lucrative opportunities. Pairing your portfolio with a blog gives you the means to share your thoughts, document what you learn, and further build your credibility.

      Using their wide range of robust features, you can build an engaging and fast portfolio using Angular and Scully. Angular is a versatile platform that allows you to create everything from web to native and mobile apps. It provides various useful tools that simplify app development, resulting in faster apps with great performance.

      Angular apps can become even faster by making them static. Using Scully, you can convert Angular apps to quicker-to-deliver Jamstack apps. Scully is a static site generator that creates a static HTML page for every route in your Angular application. These pages are faster to deliver and are effective for SEO. It also offers tools like a blog generator that you can make your portfolio blog with.

      In this tutorial, you will build a portfolio and a blog using Angular 11 and Scully. You will generate an Angular app, add pages to show your projects and profile, and add services to populate these pages. Additionally, you will generate a blog and create posts for it. Lastly, you will convert the app into a static site using Scully.

      Prerequisites

      To complete this tutorial, you will need:

      • A development environment running Node.js v12 or higher. For macOS or Ubuntu 18.04, follow the steps in How to Install Node.js and Create a Local Development Environment on macOS or How To Install Node.js on Ubuntu 18.04. For other operating systems, check out the Node.js downloads page. This tutorial was created using Node.js v16.3.0 and NPM v7.15.1.
      • Angular CLI installed, which you can do by following Step 1 of the Getting Started with Angular Using the Angular CLI tutorial. This tutorial was created using Angular CLI v11.2.14. You can install this version by running:
        command
        npm -g install @angular/[email protected]
      • Chromium installed (Windows users only). Scully requires Chromium. If you are using macOS, Ubuntu, or another operating system, by default, Scully downloads a local version that it will use to render a static site if it’s not already installed. However, if you are running Windows, you will need to install Chrome on WSL, which you can do by following this guide on WSL pre-requisites for Scully.
      • An understanding of HTML, CSS, and TypeScript, which you can find in the How to Build a Website with HTML tutorial, the How to Style HTML with CSS tutorial, and the How to Code in Typescript series.
      • Familiarity with RxJS, which you can find in the RxJS product documentation.
      • An understanding of Angular, which you can find in the Getting Started with Angular CLI tutorial.

      Step 1: Setting Up the Angular App

      In this step, you will generate the portfolio app using the Angular CLI. The CLI will scaffold the app and install all necessary dependencies to run it. You will then add dependencies like Bootstrap, Font Awesome, and Scully. Scully will make the app static. Bootstrap will provide components and styling. Font Awesome will supply icons. After installing these dependencies, you will add assets like fonts and JSON files that contain your portfolio data.

      To begin, run the following command to generate the app. It will be called portfolio. This tutorial does not include tests for the app, so you can use the -S flag to skip test file generation. If you wish to add tests later, you can exclude this flag.

      When asked whether you’d like to add Angular routing respond with yes. This tells the CLI tool to generate a separate module to handle routing for the app. It’s going to be available at src/app/app-routing.module.ts.

      You will also be prompted to pick a stylesheet format. Select CSS. Angular offers other styling options like SCSS, Sass, Less, and Stylus. CSS is simpler and that’s why you’re going to use it here.

      Output

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

      Installing Dependencies

      You will need three dependencies for this app: Scully, ng-bootstrap, and Font Awesome. Scully will convert the Angular app into a static one. The other dependencies, Bootstrap and Font Awesome, customize the look and feel of the portfolio. ng-bootstrap will provide Angular components styled using Bootstrap. This is especially useful since not all vanilla Bootstrap components work out of the box with Angular. Bootstrap also cuts down on the amount of styling you’d have to add to the application because it already provides it for you. Font Awesome will supply icons.

      When you add Scully as a dependency, it runs its init schematic which adds changes to your app in preparation for static site generation. From your project directory, use this command to add Scully to your project:

      Next, add ng-bootstrap using this command.

      The final dependency to add is Font Awesome.

      Now that the dependencies are added, you’re ready to add configuration.

      Adding Configuration

      To make Font Awesome available to the app, you will need to add a reference to its minified CSS in the angular.json file. This file can be found at the base of the project. Using nano or your favorite text editor, open this file:

      In the file, look for the architect/build section. This section provides the configuration for the ng build command. You will add the minified Font Awesome CSS reference, node_modules/@fortawesome/fontawesome-free/css/all.min.css, to the styles array. The styles array is under projects/portfolio/architect/build/options. Add the highlighted line to your file:

      angular.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"
                    ],
                }
              },
              ...
            }
          }
        }
      }
      

      Now Font Awesome will be available to the build.

      Save and close the file.

      Next, you’ll add a new font to the project, which can help personalize the look and feel of your portfolio. You can add fonts to your Angular project using Google Fonts, which provides a wide range of available fonts. You can link to selected fonts in the head tag using the link tag.

      In this tutorial, you will use Nunito font. Open src/index.html and add the lines highlighted below:

      src/index.html

      ... 
      <head>
        <meta charset="utf-8">
        <title>Portfolio</title>
        <base href="https://www.digitalocean.com/">
        <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:wght@200;400;800&display=swap" rel="stylesheet">
      </head>
      ... 
      

      The highlighted lines link to the Nunito font on Google Fonts. You’ll be getting it in three weights: extra light, regular, and extra bold.

      Save and close the file.

      Adding Bio and Project Data

      The next thing you’ll do is create the JSON files that hold all the data you’d like to put in your portfolio. Separating the templates from the data makes it easier to make changes and add more things in the future without tampering with the app.

      Begin by creating a json folder in src/assets.

      In the json folder, you will create two JSON files: bio.json and projects.json file. bio.json holds the profile you’d like to display on the site. projects.json is a list of projects you’d like to showcase.

      The structure of the bio.json file will look similar to this:

      src/assets/json/bio.json

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

      The intro is a short introduction displayed on the home page. The about is a more extended profile shown on the “About” page.

      For this tutorial, you can use an example bio or customize your own. To use a sample bio, open bio.json and add the following:

      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."
          ]
      }
      

      Save and close the file when you’re done making edits.

      The other JSON file, projects.json, will have a structure similar to this:

      src/assets/json/projects.json

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

      Each project has a name, description, a URL to where its source code is hosted, and a preview URL if it is deployed somewhere. If the project does not have a preview URL, you can just omit it.

      The stack object is used to show the language or framework the project was built off of. So the name will be the name of the language/framework and the iconClasses are the Font Awesome CSS classes for the icon of the language/framework. The featured property indicates whether the project should be displayed on the home page. If featured is set to false, it’s displayed only on the “Projects” page instead of on both the home and “Projects” pages.

      For this tutorial, you can use some example projects or add your own. To use sample projects, open projects.json and add the following:

      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
          }
      ]
      

      Save and close the file.

      Starting the Server

      To check that your app works as expected, run the server provided by the Angular CLI using this command:

      This command builds the app and serves it. If you make any changes, it rebuilds it. Once completed, the app will be served at http://localhost:4200/.

      Open your browser and navigate to http://localhost:4200. You should see a placeholder page that looks like the following:

      Image displayed when the app is started

      You should see output similar to this every time you save a change:

      ✔ 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.
      

      Once you complete a step, check that the ✔ Compiled successfully. message is present. If there is a problem, the ng serve command will output an error. When this happens, run through the step to make sure you did not miss anything or make a mistake. Once you’ve completed the tutorial, the portfolio home page should look something like this:

      Screenshot of completed portfolio home page

      In this step, you generated the app and added all the necessary dependencies, assets, and configurations. You have also started the server provided by the Angular CLI. In the next step, you will create the core module.

      Step 2: Creating the Core Module

      In this tutorial, the app you are building will contain three modules: core, blog, and portfolio. The app will be structured as follows:

      src/app

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

      The blog module is for the blog landing and blog post pages. The core module contains everything central to the app. These include the header component, data services, and data models. The portfolio module holds all your portfolio pages: the “About,” “Projects,” and home page.

      In this step, you will generate the core module. You will also generate and populate the header component, the services, and the data models. The header is displayed at the top of each page and contains the site name and menu. The models structure the portfolio data. The services fetch the portfolio data.

      This is what the core module should look like:

      src/app/core/

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

      To generate the core module, run the following command from the project root:

      This command adds the core module within the src/app/core folder and adds the module file at src/app/core/core.module.ts.

      The core module file defines the core module and its imports. In the newly generated module file, you will need to add a few imports to support the header and the services.

      Open core.module.ts and add the highlighted lines (be sure to include the comma after the CommonModule import):

      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
        ]
      })
      ...
      

      This module will use the HttpClientModule to fetch data from the JSON files you created earlier. It will also use a couple of ng-bootstrap components from NgbModule and the RouterModule for routing. You will also need to add them to the imports of the CoreModule.

      Save and close the file when you’re done.

      Generating Data Models

      In this section, you will generate data models. The data models are interfaces that you’ll use to define the structure of data from the JSON files. They will be used in the services and throughout the rest of the components as return and parameter types. You will need two models: bio, which defines the structure of your bio data, and project, which defines the structure of your project data.

      src/app/core/models

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

      The Bio model represents a profile while the Project model is a project to showcase. You will generate both models by running the following command at the project root:

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

      This command loops through the file paths and passes them to ng generate interface, which creates them in the src/app/core/models folder.

      The bio model file defines the information you’d like your bio to contain. In the src/app/core/models/bio.ts file that’s generated, add the fields that are highlighted below.

      src/app/core/models/bio.ts

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

      In this code block, you added the first name, last name, about, and introduction fields to the bio model. The first two fields are string types and the last two are arrays of strings because they might contain multiple paragraphs.

      Save and close the file.

      The project file defines the structure of a project. Here, you will list the fields you’d like to use for each project. In the src/app/core/models/project.ts file, add the lines that are highlighted.

      src/app/core/models/project.ts

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

      You’ve added fields for the project model. Each project has a name, description, a URL to its source code, and a preview URL if it is deployed somewhere. The stack object is used to show the language or framework of the project. (The name will be the name of the language/framework and the iconClasses are the Font Awesome CSS classes for the icon of the language/framework.) The featured property indicates whether the project should be displayed on the home page. If featured is set to false, it’s displayed on the “Projects” page only instead of both the home and “Projects” pages.

      Save and close the file when you’re done.

      In this section, you created the models for the data. Next, you will make services that fetch the portfolio data and use these models.

      Generating Services

      The services that you will create in this section fetch the data from the JSON files you made earlier. Once they fetch this data, components can call these services and consume the data. The models will be used as return types in these services. The bio model is used in the bio service and the project model is used in the project service. You’ll include an additional header service that will help decide what routes to use for items in the header and other components. The core module has three services: BioService, HeaderService, and ProjectsService.

      src/app/core/services

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

      To generate these services, run this command from the project’s root directory:

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

      This command loops through the file paths and passes them to ng generate service, which creates them in the src/app/core/services folder.

      The bio service fetches your bio data from the bio JSON file. To do this, you will add a method to fetch this data. Open the src/app/core/services/bio.service.ts file and add the following highlighted lines:

      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');
        }
      }
      

      The getBio method of the BioService fetches your bio from the assets/json/bio.json file. You’ll inject the HttpClient service into its constructor and use that in the getBio() method to make a GET request to the file.

      Save and close the file.

      Next, you will modify the HeaderService. The header service is used to check whether the current route is the home page. You will add a method that determines whether the current page is the home page. Open the src/app/core/services/header.service.ts file and add the highlighted lines:

      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 == "https://www.digitalocean.com/";
        }
      }
      

      In the HeaderService, the isHome method checks whether the current page you are on is the home page. This is useful for scrolling to an anchor and showing featured projects on the home page.

      Save and close the file.

      Finally, you will modify ProjectsService. The projects service fetches the project data from the projects JSON file. You will add a method to fetch the projects data. Open the src/app/core/services/projects.service.ts file and change the contents to the following:

      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$;
        }
      }
      
      

      The ProjectsService has a getProjects method that gets and filters projects. It gets the projects from the assets/json/projects.json file. You’ll inject the HttpClient service into its constructor and use that in the getProjects() method to make a GET request to the file. Using the featured parameter, you can choose to only return featured projects for brevity. This is useful on the home page when you only want to show important projects.

      Save and close the file.

      In this section, you added the bio and project data services that fetch bio and project data from the bio and projects JSON files. You also created a header service that checks whether the current page is the home page. In the next section, you will create a header that will be displayed at the top of each page of your portfolio. It will use the bio and header services.

      The header component is displayed at the top of all pages. It contains your name and links to the “About” and “Projects” pages as well as the blog. The bio and header service will be used in the header. The bio service will provide bio data to the header. The header service will be used to check whether the current page is the home page and will set links to the “About” and “Projects” sections or pages based on that. You will generate it by running this command from the project root:

      • ng generate component core/header

      The header component is displayed at the top of each page. It will use the bio service to get your first and last name. It will also use the header service to determine whether the current page is the home page. Using this information, it will set links to the “About” and “Projects” sections or pages.

      In the header.component.ts file, you will inject the bio and header services and add a styling property to handle the responsiveness of the component on different screen sizes.

      Open header.component.ts and add the highlighted lines:

      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: "https://www.digitalocean.com/", fragment: 'about', pagePath: '/about' },
          { title: 'My Projects', homePath: "https://www.digitalocean.com/", fragment: 'projects', pagePath: '/projects' },
          { title: 'My Blog', homePath: '/blog', fragment: '', pagePath: '/blog' }
        ];
      
        constructor(private bioService: BioService, private headerService: HeaderService) { }
      }
      

      In this component file, you will inject two services: the bioService to get your name from the bio JSON file and the headerService to figure out if the page currently displayed is the home page. The latter allows you to decide whether the buttons should go to a separate page like /projects or perform anchor scrolling like with /#project. menuItems contains all the menu items to display. The bio$ and isHome$ properties hold observables from the aforementioned services.

      Save and close the file.

      Next, you will modify the template for the header component. Here is where the data fetched from the bio service is displayed. Links to the “About” and “Projects” sections or pages are also added here. In the src/app/core/header/header.component.html template file, add the code below.

      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="https://www.digitalocean.com/">
              <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>
      

      In the template, your names (bio.firstName and bio.lastName) are displayed using data from the bio property. Depending on the size of the screen, either a dropdown or a list of buttons from menuItems is shown. The alias pipe in the template will handle unsubscriptions from the observables. This pattern will be followed throughout this tutorial.

      Save and close the file.

      The header has to be visible on all pages. To make this happen, you will need to take a couple of steps. First, CoreModule needs to export HeaderComponent to make it accessible. To export it, add the highlighted lines to src/app/core/core.module.ts. Don’t forget to add the comma after the imports array.

      src/app/core/core.module.ts

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

      To make the header visible, you will also need to add it to the AppComponent template, which is in the AppModule. AppModule also has to import CoreModule to have access to the header. You will complete these additional tasks in a later step.

      In this step, you created models that organize your portfolio data. You also created services to fetch the portfolio data using the models. Additionally, you made a header service that helps decide what routes to use for header items. Lastly, you generated a header component to be displayed on all portfolio pages. In the next step, you will generate the portfolio module, which contains all the primary pages of your portfolio. The pages in the portfolio module will use the bio and projects services and models you created in this section.

      Step 3: Generating the Portfolio Module

      In the previous step, you created the core module, which holds the header and contains all the services and models that you will use to fetch the portfolio data. In this step, you will generate the portfolio module, which contains all the essential pages of the portfolio. These include the home, “About,” and “Projects” pages. You will use the services and models you made in the core module to create these pages in this step. You will also add routes for each of the pages.

      The home page will display a summary of your profile using the header and bio services. The “About” page is a more in-depth profile, and will use the bio service and model. The “Projects” page showcases your projects, using the projects service and model to display your projects. At the end of this step, your portfolio module will be structured as follows:

      src/app/portfolio

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

      First, you will generate two modules: a portfolio module and a portfolio routing module. The portfolio module contains all the primary pages of your portfolio. The portfolio routing module is responsible for routing to these pages.

      To generate both modules, run this command from the project root:

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

      This command creates the app/portfolio folder and adds a module file at app/portfolio/portfolio.module.ts. You will see this route added in app/src/app-routing.module.ts. The --routing flag specifies that the portfolio routing module be generated. This routing module will be located at app/portfolio/portfolio-routing.module.ts.

      The --route flag creates a lazy-loaded route in the app module, as specified by the --module flag. You will see this route added in app/src/app-routing.module.ts. It also adds a placeholder component for routing purposes that’s discussed in the next section.

      This portfolio module should be available at the / path. This requires that you supply the --route flag with an empty string, like --route="". However, ng generate module doesn’t allow empty strings for the --route flag. So you will have to use a placeholder, portfolio. You will then replace this placeholder with an empty string in src/app/app-routing.module.ts, which handles routing for the whole app.

      Open src/app/app-routing.module.ts and replace the highlighted lines:

      src/app/app-routing.module.ts

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

      This ensures that all the pages in the portfolio module are available starting at the / path.

      Save and close the file.

      Creating the Home Page

      The command that creates the portfolio module also creates a PortfolioComponent. This is a placeholder component that’s used when you are setting routing for the module. However, a more appropriate name for this component would be HomeComponent. The home page is the landing page of your portfolio. It will have a summary of your whole portfolio. This makes it easier for users to get an overview of your work without having to navigate to multiple pages, reducing the risk of losing interest.

      To change the name of this component, you will first create a new folder to house it. From the project root, run the following command:

      • mkdir -p src/app/portfolio/home

      Next, you will move all the PortfolioComponent files into this new folder.

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

      This command moves all the files with names that begin with portfolio.component.* into the src/app/portfolio/home/ folder.

      You will then rename the portfolio.component.* files to home.component.*.

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

      After you run the above commands, you will get some errors because of the change in the component’s name and path. You have to make some changes to a couple of files to fix this: the portfolio routing module, the portfolio module, and the home component files. In these files, you will change all instances of PortfolioComponent to HomeComponent. You will also update the paths from ./portfolio.component to ./home/home.component.

      Start by opening src/app/portfolio/portfolio-routing.module, which handles routing for the portfolio module. Make the highlighted changes:

      src/app/portfolio/portfolio-routing.module

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

      Save and close the file.

      Next, open src/app/portfolio/portfolio.module.ts, the portfolio module file. Make the highlighted changes:

      src/app/portfolio/portfolio.module.ts

      ...
      import { HomeComponent } from './home/home.component';
      
      
      @NgModule({
        declarations: [
          HomeComponent
        ],
        ...
      })
      ...
      

      Save and close the file.

      Finally, open src/app/portfolio/home/home.component.ts, the home component file. Make the highlighted changes:

      src/app/portfolio/home/home.component.ts

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

      Save and close the file.

      In these files, you changed all instances of PortfolioComponent to HomeComponent and updated the paths to point to HomeComponent. After doing all this, the portfolio module should look like this.

      src/app/portfolio

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

      You’ve now updated the names and paths to the home component files.

      Next, you will populate the home component template with content and style it. The home component is the main page of the portfolio and displays a profile summary. (This is the component that was renamed from portfolio component to home component above.) In this component, you will need to fetch the bio data to display and add styling to make the page responsive on different screen sizes.

      Open src/app/portfolio/home/home.component.ts and update the code to match the following:

      src/app/portfolio/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) { }
      }
      

      The home page will display your name and a short bio, which is retrieved from the BioService you inject here. Once you call its getBio method, the resultant observable will be stored in the bio$ property. The respOptions property stores config that helps ensure that the view is responsive.

      Save and close the file.

      Next, you will modify the template for the home component. It is responsible for displaying the information from the bio service across responsively different screen sizes. You will add your name, a brief intro, and the about and projects components that will be covered later.

      Open src/app/portfolio/home/home.component.html and add the following code:

      src/app/portfolio/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="https://www.digitalocean.com/" 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>
      

      In this template, you display the names bio.firstName+ and +bio.lastName as well as an introduction, bio.intro from the bio. You are also showing the about component app-about and the projects component app-projects, which you will generate in the next step.

      Note: There are a few other components added to this template that do not exist yet. These are the About and Projects components. These are what you will add next. If you are running the server, this will generate an error. You can comment these lines out until after you’ve generated them.

      src/app/portfolio/home/home.component.html

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

      Next, you can add styling for the home component. Open src/app/portfolio/home/home.component.css and add these lines:

      src/app/portfolio/home/home.component.css

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

      Here you are adding styling to the home component so that there is some space between the main contents of the home page and the edges of the browser window.

      Once completed, the home page will look like this (you’ll be able to preview the site in the last step):

      Home Page

      In this step, you created the home page that displays a summary of your portfolio. In the next section, you will generate the “About” and “Project” components. These will be displayed on the home page and will be used as standalone pages as well.

      Generating the About and Project Pages

      Instead of generating every page individually, you can run a single command to make the remaining “Projects” and “About” pages all at once. You do this by running the following command from the project root:

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

      This command will loop through each of the page names and generate them.

      Populating the About Page

      The “About” page will display a more in-depth profile of you. The information on this page is retrieved from the bio service and uses the bio model as well. This component will be displayed on the home page. It will also be a standalone page with its own route.

      To populate the “About” page with your bio, you will modify the “About” component file to use the bio service. You will also set options to make the page responsive across different displays. Open src/app/portfolio/about/about.component.ts and add the highlighted lines:

      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) { }
      }
      

      The “About” information will come from the BioService, and once its getBio method is called, the observable will be stored in the bio$ property. respOptions helps with responsiveness by providing optional CSS classes for different display sizes.

      Save and close the file.

      Next, you will modify the template for the “About” page so that you can display the information retrieved from the bio service. Open src/app/portfolio/about/about.component.html and add the following lines:

      src/app/portfolio/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>
      

      In this template, you will display the data from the bio$ observable. You will loop through the “About” section of the information and add it as paragraphs to the “About” page.

      Save and close the file.

      Once completed, the “About” page will look like this (you’ll be able to preview the site in the last step):

      About Page

      Populating the Projects Page

      The “Projects” page will show all your projects, which are retrieved from the projects service. This component will be used on the home page and will also be a standalone page. It will be displayed on the home page together with the “About” component. When this component is used on the home page, only featured projects should be visible. There exists a See More Projects button that will only appear on the home page. When clicked, it redirects to a full-list projects page.

      To populate the “Projects” page, you will modify its component file to get projects from the projects service. You will also use the header service to determine whether to display all or highlighted projects. You will also add options to make the page responsive across different screen sizes. Open src/app/portfolio/projects/projects.component.ts and add the highlighted lines:

      src/app/portfolio/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) { }
      }
      

      The projects come from the ProjectsService. You will use the HeaderService to determine whether the current page is the home page. You will use the value of isHome$ to determine whether to fetch a full list of projects or just featured projects.

      Save and close the file.

      Next, you will modify the template for the projects components. Using the projects you got from the projects service, you will loop through and add them here. You will display basic information about each project in a card and add links to where their code is hosted and where you can preview them.

      Open src/app/portfolio/projects/projects.component.html and add the following lines:

      src/app/portfolio/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" How To Build a Jamstack Portfolio with Angular 11 and Scully="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>
      

      Here you add each project from projects$ to a card. In the card, you will display the project name (project.name), the technology stack used in it (project.stack), and a brief description (project.description) of what it does. You will also add links to where the code for the project is hosted. Additionally, you will add a link to where the project can be previewed if it is deployed. Lastly, there is a See More Projects button that is displayed only on the home page. On the home page, only featured projects are displayed. When this button is clicked, a user is routed to a full list of projects.

      Save and close the file.

      Next, you’ll style the project cards by modifying the projects template. Open src/app/portfolio/projects/projects.component.css and add the following lines:

      src/app/portfolio/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;
      }
      

      Here, you set the size of the project card and the project titles, which tend to be a bit longer.

      Once completed, the full-list “Project” page will look like this (you’ll be able to preview the site in the last step):

      Full-list Projects Page

      Adding the Rest of the Portfolio Routes

      To make each page accessible, you will need to create a route for each one. You will add these in the PortfolioRoutingModule, which handles routing for the PortfolioModule. The “About” page should be available at /about and the “Projects” page at /projects.

      To create routes for the portfolio module pages, you’ll modify the portfolio routing module file, which is responsible for routing. Open src/app/portfolio/portfolio-routing.module.ts and add the highlighted lines:

      src/app/portfolio/portfolio-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 { }
      

      Here, you added routes to the “About” and “Projects” pages by specifying paths to the components and adding them to the routes array.

      In this step, you completed the portfolio module by creating each of its three pages and adding routes for them. In the next step, you will generate the blog component.

      Step 4: Generating the Blog Module

      In this step, you will generate the blog module, which contains your blog landing and post pages. Instead of building the blog from scratch, you will use a Scully schematic to set up all that’s required for a functioning blog. The Scully schematic generates the module, adds a routing module to handle routing to the blog, and creates a blog component that displays a blog post. The blog component will display posts that you write in markdown files. You will see where these markdown files reside in a later step when you create new blog posts. When rendering the blog, Scully takes markdown versions of blog posts you created and converts them into static HTML pages, which are faster to deliver to readers.

      You can enable blog support for the app and generate the module by running the following command from the project root:

      • ng generate @scullyio/init:blog

      The above command creates the blog module at src/app/blog, makes a blog folder at the project’s base where blog markdown files will reside, adds a lazy-loaded route for the module in AppRoutingModule, and creates a blog component at the base of the module.

      Next, create a folder within the module where the blog component will reside.

      To move the blog component into this folder, run:

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

      This will result in this blog module structure:

      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
      

      Since this module has been restructured, some paths will be broken and will need updating. Two files, blog-routing.module.ts and blog.module.ts, will have to be updated with the new paths to the BlogComponent.

      Open blog-routing.module.ts and update the import as shown:

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

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

      Save and close the file.

      Next, open blog.module.ts and update the import as shown:

      src/app/blog/blog.module.ts

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

      Save and close the file.

      Next, you will modify the template for the blog component. The blog component’s role is to display a blog post. This component requires very minimal editing as the Scully blog schematic already populates it. You will add styling to the container that will hold blog post content. Open src/app/blog/blog/blog.component.html and replace the boilerplate content with the following lines:

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

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

      The styling added to the template will make the blog component better spaced within the page. <scully-content></scully-content> will render the markdown blog content.

      Save and close the file.

      Next, you will modify styling by centering the headings, which creates a better look and feel to the blog component. Open src/app/blog/blog/blog.component.css and replace the content with these lines:

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

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

      Save and close the file.

      Once completed, the blog will look like this (you’ll be able to preview the site in the last step):

      Blog Post Page

      Generating the Blog Landing Page

      Now that you have created the blog module and have added styling to blog posts, you will generate the blog landing page and add styling to the landing page.

      The blog landing page will list all your blog posts. You can generate it by running the following command at the project root:

      • ng generate component blog/blog-landing

      This will result in this structure:

      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
      

      Next, you will modify the component file for the blog landing page to list all the blog posts. Here you will get all the pages that have a /blog/ in their route and display them in a list. You will also add options to make the page responsive across different screen sizes.

      Open src/app/blog/blog-landing/blog-landing.component.ts and make the following changes:

      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) { }
      }
      

      To get a list of all blog routes, you will use the ScullyRoutesService. The available$ observable will return all the routes rendered by Scully and marked as published. You can mark whether a blog post is published or not in its markdown file frontmatter. (This will be covered in the next step.) This observable will return all routes, including those from the portfolio. So you will filter only routes containing the prefix /blog/. The blog routes will be held by the links$ property. The respOptions property will help with responsiveness.

      Save and close the file.

      Next, you will modify the template for the blog landing page to list all the available blog posts in cards and link to them. It also contains the title of the blog. Open src/app/blog/blog-landing/blog-landing.component.html and add the following lines:

      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" How To Build a Jamstack Portfolio with Angular 11 and Scully="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>
      

      In this template, you will loop through all the blog posts returned by the Scully router service. For each blog post, you will add a card. In each card, the title of the blog post and a description are displayed. There is also a link added which can be clicked to go to the blog post.

      Save and close the file.

      Finally, you will add styling to the blog landing template. It will style the project cards that are added to the page. Open src/app/blog/blog-landing/blog-landing.component.css and add the following lines:

      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;
      }
      

      Save and close the file.

      Once completed (and after you have added blog posts), the blog landing page will look like this (you’ll be able to preview the site in the last step):

      Blog Landing

      Adding the Blog Landing Route

      To make the blog landing page accessible at the /blog path, you will have to add a route for it in the BlogRoutingModule. Without adding this, it will not be available to the app. Open src/app/blog/blog-routing.module.ts and add the highlighted lines:

      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 }
      ];
      ...
      

      Here you added the route for the BlogLandingComponent to the routes array. This will make it accessible at the /blog route.

      Save and close the file.

      In this step, you created a blog module that contains two pages: a blog post page and a blog landing page. You added styling to these pages and added the blog landing route so that the landing page will be accessible at the /blog path. In the next step, you will add new blog posts.

      Step 5: Adding New Blog Posts

      In this step, you will use Scully to generate new blog posts that will be displayed on the blog landing page. With Scully, you can generate markdown files that will serve as your blog posts. The blog component you generated in the previous step will read the markdown version of a blog post and then display it. Markdown makes it easy to write rich formatted blog content quickly and easily. Scully will create these files as well as add folders to house them for you. It will also add metadata like a title and description to each post. Some of the metadata is used to determine how a post should be displayed. Later, you will use Scully to generate static HTML page versions of these markdown blog posts.

      Before you can make a post, you need to come up with a name. For this tutorial, you’ll create a post titled “Blog Post 1”. You will provide this name to the command below using the --name flag from the project root.

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

      The output will look similar to this:

      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)

      This will create a /blog/blog-post-1.md file at the project root. The contents of the file will look similar to this:

      blog/blog-post-1.md

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

      Once you’ve added content to your blog post and are satisfied with it, you can change published to true and it will appear on the blog landing page when you render the site. To view posts that are still unpublished, you can use the slug property.

      For example, suppose you added this 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
      

      You would be able to view this post at https://localhost:1668/blog/alternate-url-for-blog-post-1 when you run the server. However, this unpublished post would not show up on the blog landing page unless marked as published: true. When generating Scully routes as you will see in a later step, Scully will add a slug for all your unpublished posts so you do not have to.

      To add content to your post, start after the title. All the post content needs to be in markdown. Here’s an example of content you can use in the markdown post you generated:

      /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.
      

      Save and close the file when you’re done.

      You can generate other posts by running:

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

      These commands will create two other markdown files in the /blog/ folder with the names you assigned. You can populate the generated files with the sample content above as you did in the first post.

      In this step, you created your first Scully blog post. The following step will cover changes you’ll need to make to complete the app.

      The last thing to do before previewing the app involves enabling anchor scrolling, adding global styling, and cleaning up app.component.html.

      On the home page, when a visitor clicks the items in the header, they should be directed to the specific sections on the same page. To make this happen, you need to enable anchor scrolling on the Angular app. Making all these changes will make scrolling to sections of the home page possible.

      First, you will modify the module file for the app routing module. This module is responsible for routing throughout the entire app. Here you will enable anchor scrolling. Open src/app/app-routing.module.ts and add the highlighted portion:

      src/app/app-routing.module.ts

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

      Adding { anchorScrolling: 'enabled' } enables anchorScrolling on the router module so you can jump to different sections on the home page.

      Save and close the file.

      When you generate the Angular app, the template for the main app component (src/app/app.component.html) contains placeholder content. This placeholder content is displayed on all the pages of your portfolio. It looks something like this:

      Placeholder content on app component

      Since you won’t be needing this placeholder content on your portfolio, you will remove this.

      To remove generated placeholder code from the main page, open src/app/app.component.html and replace its contents with the following lines:

      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>
      

      In this file, you add app-header, the header component, and placed a container div around router-outlet so that routed pages are displayed under it.

      Next, you’ll need to ensure that AppModule has access to app-header. Since app-header exists in a different module, App Module does not currently have access to it. You will need to add CoreModule as an import to src/app/app.module.ts because CoreModule provides access to the header component. Open app.module.ts and add the import as highlighted below.

      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 { }
      

      Making this change ensures that AppModule has access to app-header.

      Finally, you’ll make some adjustments to the global styling for the app by modifying src/styles.css. Several components across the app use the styling in this file. It contributes to the overall look and feel of the app and prevents repetition because styling is reused across components.

      Before proceeding to run the site, open src/styles.css and add the following lines:

      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;
      }
      

      In this file, you ensure that html and body take full-page heights and widths. You also make Nunito the default font and include various style classes for setting widths and heights.

      In this step, you enabled anchor scrolling, added global styling, and cleaned up the app component template. In the next step, you will build the site, render Scully routes, and serve the static portfolio.

      Step 7: Previewing the Static Site

      Now that you have completed all the necessary code changes, you can preview your portfolio with Scully. This will involve building your site, generating the Scully routes, then serving the static version of the site. In this step, Scully pre-renders your Angular app into a static site and provides a server to serve both the Angular app and the static portfolio.

      Before Scully can pre-render your portfolio, you will need to build it.

      This command will compile your portfolio to dist/portfolio.

      The output will look similar to this:

      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
      

      When the build completes, run:

      Scully will pre-render the whole portfolio by taking each route and creating a separate index.html for each of them. The pre-rendered portfolio will be located in dist/static. This folder should resemble this. (Some files have been removed for clarity.)

      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
      

      Notice how each route has its own separate index.html file.

      To preview the static site, run:

      This command will start a static Scully server on http://localhost:1668/ and serve your static portfolio. (Once you’re done previewing your site, you can kill the server with Ctrl + C on the terminal where the server is running.)

      Note: Scully might have a problem locating Puppeteer. This happens when it tries to run the app in a restricted environment like on a CI service or a virtual machine in the cloud. You may get this error if you attempt to run the app on a DigitalOcean Droplet. The error looks something like this:

      =================================================================================================
      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
      =================================================================================================
      

      To fix this, add the highlighted portions to 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']}
      };
      

      The puppeteerLaunchOptions option makes it possible to change Puppeteer’s default options and overwrite them with ones that will work in your environment. The --no-sandbox and --disable-setuid--sandbox disable the multiple layers of sandboxing provided for Puppeteer. You can read more about this Chrome troubleshooting resources. Depending on your setup, you may also need to install additional dependencies to run Chromium, which you can learn more about in the Puppeteer troubleshooting guide.

      This is what the home page at http://localhost:4200 should look like:

      Home Page

      In this step, you built your Angular app, pre-rendered it into a static site, and served it using Scully.

      Conclusion

      In this tutorial, you generated and configured an Angular portfolio app. You also created a core module to handle your portfolio data and hold components central to the app. Moreover, you made a portfolio module consisting of essential pages showcasing your bio, projects, and profile. You built a blog module made of your blog landing and post pages. Lastly, you converted your Angular portfolio into a static site using Scully.

      There’s still so much to do with your portfolio. You can add pages to show your skills and articles you’ve written. You can also add a contact page so people can get in touch with you. If you have speaking engagements, a video tutorial channel, a podcast, or conference talks, you could create pages to show them off.

      Additionally, Scully offers other useful features like syntax highlighting integration for code in your blog posts. You can learn more about syntax highlighting with prismjs in the Scully product docs.

      Finally, you could add tests and deploy the portfolio. You can view a live version of this app at the author’s GitHub. The source code for this project (as well as a more advanced version) is available on GitHub. To find out how to deploy a static site like this on DigitalOcean, check out these tutorials on App Platform.

      Note: This tutorial was created using Angular major version 11. Consider upgrading to a more recent version compatible with the most up-to-date version of Scully. You can use Angular’s update tool to figure out how to do this. You might also consider updating other dependencies used in the tutorial, such as Font Awesome and ng-bootstrap.



      Source link

      Getting Started With Angular Using the Angular CLI


      Introduction

      Angular is a popular JavaScript framework that creates interactive web, mobile, and desktop applications. Enforced separation of concerns, first-class TypeScript support, and robust Command Line Interface (CLI) make Angular a top choice for modern enterprise applications.

      The Angular CLI simplifies the process of getting started with Angular. The CLI also takes care of code generation, production builds, and dependency management.

      By the end of this tutorial, you’ll have created an Angular application with the Angular CLI. You’ll use the CLI to create a new app, run it in the browser, and build for production.

      You’ll also view how Angular splits presentation, styling, and business concerns into separate units. This involves practicing how HTML, CSS, and TypeScript layers interact with each other.

      Prerequisites

      To follow along with the article, you’ll need the following:

      Step 1 — Installing the Angular CLI and Creating Your First Application

      In this step, you’ll install the Angular CLI on your local computer and build out your Angular application. To begin, you’ll download the Angular CLI and achieve the following three goals:

      • Create a new Angular project

      • Run the local development server

      • Build this application for production

      Run the following command in your terminal to download the Angular CLI:

      • npm install -g @angular/cli

      To ensure the installation was successful, run the following command in your terminal:

      The Angular CLI will print the current version and surrounding environment to the terminal:

      Output

      Angular CLI: 12.1.4 Node: 14.17.4 Package Manager: npm 6.14.14 OS: darwin x64

      To begin working with Angular, you’ll create your first workspace with the CLI called my-first-angular-app. Projects in Angular are known as workspaces.

      Run the following in your terminal:

      • ng new my-first-angular-app

      The command will ask some initialization questions used to configure the workspace:

      Output

      ? Would you like to add Angular routing? (y/N) N ? Which stylesheet format would you like to use? ❯ CSS SCSS [ https://sass-lang.com/documentation/syntax#scss ] Sass [ https://sass-lang.com/documentation/syntax#the-indented-syntax ] Less [ http://lesscss.org

      For the first question, type in N as you won’t use Angular routing for this project. Press your ENTER or RETURN key.

      In the second question, select CSS as the stylesheet format.

      Press your ENTER or Return key to initialize your project.

      You’ll now have an Angular workspace named my-first-angular-app. The Angular CLI installs any required dependencies on project creation. For more information about the Angular CLI and workspace creation, check out the [Angular documentation]((https://angular.io/cli)).

      Now that you’ve downloaded the Angular CLI and made a new Angular workspace, let’s look at how to run your application in the browser.

      Step 2 — Running Your Angular Application in the Browser

      In this step, you’ll run your application in the browser using the CLI. Angular’s local development server refreshes the browser whenever the filesystem changes. The fast feedback cycle ensures that you can see the results of your changes as soon as possible.

      Angular’s local development server provides you with the following benefits:

      • Starts a local web server on a port of your choice.
      • Watch for changes in the files and reload the browser on change.
      • Manages TypeScript compilation and bundling.

      Ensure you’re in the my-first-angular-app directory by running this command in your terminal:

      Next, start the web server by running the following command in your terminal:

      Angular will build your development application and expose it on a local web server at http://localhost:4200. The application will open in a web browser because you added the -o flag to ng serve.

      You can verify that this was successful by examining the output of your terminal:

      Output

      ✔ browser application bundle generation complete. Initial Chunk Files | Names | Size vendor.js | vendor | 2.08 MB polyfills.js | polyfills | 128.57 kB main.js | main | 10.81 kB runtime.js | runtime | 6.59 kB styles.css | styles | 179 bytes | Initial Total | 2.22 MB Build at: 2021-08-01T23:30:14.012Z - Hash: 3302767a539a29a592de - Time: 7433ms ** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ ** ✔ Compiled successfully.

      You can pass other configuration options to ng serve to customize the environment. You can see a complete list of configuration options here.

      You’ll now see your Angular application on the screen. As long as ng serve is running, any changes to the file system will cause the browser to update with the new content. Keep ng serve running throughout the development process of your application.

      In this step, you ran your Angular application in the browser using ng serve. You passed configuration options to CLI commands, such as `ng serve -o’.

      Next, you’ll make changes to an Angular component and see the changes reflected on the screen.

      Step 3 — Updating the Homepage Template

      In this step, you’ll be displaying content on-screen by editing HTML. The HTML template is otherwise known as the view of an Angular component. By the end of this step, you’ll have learned how to update the starter template with your content.

      You can find the HTML for your AppComponent at src/app/app.component.html. Open this up in a text editor of your choice and delete the contents. You should have a blank HTML document ready to write your markup.

      Add the below HTML copy to your src/app/app.component.html:

      src/app/app.component.html

      <div>
       <article>
        <header>
          <h1>I'm learning Angular at DigitalOcean</h1>
        </header>
        <section>
          <p>Here's what I've learned so far:</p>
          <ul>
            <li>
              Angular allows us to create performant web applications using TypeScript.
            </li>
            <li>
              I can use the Angular CLI to create/interact with my Angular workspace.
            </li>
            <li>
              Making changes will automatically be shown on screen, thanks to the local development server!
            </li>
          </ul>
        </section>
       </article>
      </div>
      

      You’ve added a <div> containing a header and a section that includes an unordered list. You will see your unordered list in bullet points and your title in large font due to the <h1> tag.

      Notice that your app component updates as soon as you save the document. Feel free to experiment with further additions of your own and witness them in real-time.

      You’ve now updated the HTML template of the base app component. This is the entry component for your Angular application. In the next step, you’ll be styling the content using CSS.

      Step 4 — Updating the Homepage Styling

      In this step, you’ll style your application with CSS. Angular allows you to work with both scoped and global styles. The ability to scope CSS to a component enables flexibility without impeding your designs.

      You’ll start by setting up some default global styles for your application by editing src/styles.css:

      src/app/app.component.css

       body { background-color: #fce7f3;
       display: flex;
       align-items: center;
       justify-content: center;
       height: 100vh;
       font-size: 2rem;
      }
      

      These styles centers all of your content in the middle of the screen, and sets a warm background color. Keep in mind that any changes made to your global styles.css will show in every component going forward. If successful, you will view the following in your browser:

      Global styling

      Note: Remember to save your files after each change before moving to the next step.

      The Angular CLI adds styles.css to the styles array of the build builder in your angular.json file. You don’t have to manually add a reference to styles.css to index.html.

      angular.json

      {
       "projects": {
       "my-first-angular-app": {
       ...
       "architect": {
       "build": {
       "builder": "@angular-devkit/build-angular:browser",
       "options": {
       ...
       "styles": ["src/styles.css"]
                }
              }
            }
          }
        }
      }
      

      You’ll review more about the Angular build process in Step 6.

      Next, let’s style the app component. Styles added to a component won’t appear in other parts of your application. For more information on style scoping, read through the Angular documentation.

      Head over to src/app/app.component.css and add the following CSS below the global style:

      src/app/app.component.css

      ... article {
       background-color: #fbcfe8;
       padding: 2rem 4rem;
       color: #1f2937;
       font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
       box-shadow: 20px 20px 0px #db2777;
       border: 6px solid #831843;
       border-radius: 2rem;
      }
      
      header {
       text-decoration: underline;
      }
      
      ul {
       list-style: square;
      }
      

      Your style will now appear in the content within the <article> tag in your browser:

      Component styling

      With your styling in place, let’s explore how to change your user interface dynamically.

      Step 5 — Updating the Homepage TypeScript

      In this step, you’ll work with TypeScript in your Angular application, found at src/app/app.component.ts. Angular uses decorators to dynamically style your components with ngStyle and ngClass.

      Angular components use the JavaScript class syntax. To tell Angular that this class is a component, you’ll need to add a @Component(...) decorator to the class.

      Decorators in TypeScript and Angular provide metadata around the class or function declaration. In Angular, they are specifically used to provide metadata to the dependency injection system and the compiler. This allows arbitrary code to run before, during, and after executing a class or function. Angular then associates the files that connect with a component. You can find more information about decorators in TypeScript here.

      You can see this in action in the AppComponent created with the CLI. Open src/app/app.component.ts and observe the following:

      src/app/app.component.ts

      @Component({
        selector: 'app-root',
        templateUrl: './app.component.html',
        styleUrls: ['./app.component.css']
      })
      export class AppComponent {
       ...
      }
      

      Notice that the styleUrls property is an array with one value. This is because app-root contains one CSS style sheet. You could add more style sheets to the styleUrls array, and Angular will bundle them together.

      The selector is the CSS selector that identifies this component. This will be the name of the component in the HTML template, and you can see this inside of index.html:

      index.html

      <!doctype html>
        <html lang="en">
          <head>
            ...
          </head>
          <body>
            <app-root></app-root>
          </body>
      </html>
      

      Angular allows you to bind application state defined inside of your src/app/app.component.ts file to its HTML template at src/app/app.component.html.

      Within the AppComponent, create a new variable named myNextProject and assign it the value of a porfolio website:

      src/app/app.component.ts

      import { Component } from '@angular/core';
      
      @Component({
        selector: 'app-root',
        templateUrl: './app.component.html',
        styleUrls: ['./app.component.css']
      })
      export class AppComponent {
        myNextProject="a portfolio website.";
      }
      

      You can now use the variable as a binder to src/app/app.component.html and connect the data defined in TypeScript to your HTML template. Angular defines TypeScript variables in HTML by surrounding the variable inside double curly braces:

      {{ myNextProject }}
      

      Within src/app/app.component.html, add the variable in the text between the <footer> tag:

      src/app/app.component.html

      <article>
        <header>
          <h1>I'm learning Angular at DigitalOcean</h1>
        </header>
        <section>
          <p>Here's what I've learned so far:</p>
          <ul>
          <li>
            Angular allows you to create performant web applications using TypeScript.
          </li>
          <li>
            I can use the Angular CLI to create/interact with my Angular workspace.
          </li>
          <li>
            Making changes will automatically be shown on screen, thanks to the local development server!
          </li>
        </ul>
       </section>
       <footer>
          <p>For my next project, I'll be making {{ myNextProject }}</p>
       </footer>
      </article>
      

      The content within the <p> tag will render as For my next project, I'll be making a portfolio website. in the browser:

      Binding to template variables

      As executed here, binding values in your template allows you to serve dynamic content to your users.

      You can also use functions inside of your TypeScript file to determine your outputs as well. In your src/app/app.component.ts, define a new variable showPurpleBoxShadow and a getBoxShadow() function:

      src/app/app.component.ts

      import { Component } from '@angular/core';
      
      @Component({
        selector: 'app-root',
        templateUrl: './app.component.html',
        styleUrls: ['./app.component.css']
      })
      export class AppComponent {
        myNextProject="a portfolio website.";
      
        showPurpleBoxShadow = true;
      
       get boxShadow() {
       if (this.showPurpleBoxShadow) return '20px 20px 0px #bdb2ff';
      
       return '20px 20px 0px #DB2777';
        }
      }
      

      Navigate to your src/app/app.component.html file and insert the [ngStyle] directive within your <article> tag:

      src/app/app.component.html

      <article [ngStyle]="{'box-shadow': boxShadow}">...</article>
      

      ngStyle is an Angular directive that binds application state to CSS styles. In Angular, a directive is a “marker” on a DOM element, such as [ngStyle]. Angular is then able to attach functionality to the DOM element.

      In this scenario, you’re applying different box-shadow styles depending on the value of showPurpleBoxShadow. You can also achieve the same result with the use of the ngClass directive. Instead of applying CSS styles, this will conditionally apply a CSS class.

      Open src/app/app.component.css and add the following CSS classes:

      src/app/app.component.css

      .purple-box-shadow {
       box-shadow: 20px 20px 0px #bdb2ff;
      }
      
      .pink-box-shadow {
       box-shadow: 20px 20px 0px #db2777;
      }
      

      Then, in src/app/app.component.ts, update the boxShadow getter to return a class name instead:

      src/app/app.component.ts

      export class AppComponent {
       ...
      
       get boxShadow() {
       if (this.showPurpleBoxShadow) return 'purple-box-shadow';
      
       return 'pink-box-shadow';
        }
      }
      

      You can then use the ngClass directive to conditionally apply the purple-box-shadow class to the <article> tag:

      src/app/app.component.html

      <article [ngClass]="boxShadow">
      ...
      </article>
      

      If successful, you will view the following in your browser:

      Dynamic styling with ngClass

      You can now see your design manifest on-screen without additional redeployment after each change. Let’s look at how to build your Angular application for development and production.

      Step 6 — Building Your Production Environment

      In this step, you’ll use the Angular CLI to generate a production build for your Angular project. Angular uses Webpack, which you will use to bundle your files and optimize the site for production.

      Webpack is an open-source module bundler for JavaScript applications. It provides the ability to load code/assets on demand. It offers a powerful optimization system that minimizes the number of requests needed to fetch all necessary assets from the server.

      To create a custom Webpack configuration file, review the article How To Use Custom webpack Configurations with Angular CLI Builders for more information.

      By default, the Angular CLI offers two types of builds:

      Development

      The unoptimized version of your code containing source maps and runtime checks that help you detect and investigate issues while developing an app before deploying it into production environments.

      The Angular CLI will generate an unoptimized version of your code when you run the command ng serve. You should avoid deploying the development build into production. The development build doesn’t contain any optimizations that are necessary for a fast, production-ready application.

      You can build your project with the development configuration by running the following command in your terminal:

      • ng build --configuration development

      Production

      Building your project with the production configuration will generate optimized code and provides you with benefits like:

      • Code minification and bundling to decrease your application’s size.
      • Tree-shaking to make unused parts of imported modules excluded from final builds, saving bandwidth with no detrimental impact on functionality.
      • No source maps, compiler errors, and warnings for a smaller bundle size.

      Production builds are essential for public-facing applications, as page speed and delivery time are crucial metrics for search engines and users. For more information on page speed and delivery time optimization, click here.

      To generate a production build, run ng build from your project folder. The CLI will lint your code and generate an optimized, minified version of your application.

      If successful, it’ll provide you with statistics about the bundle:

      Output

      ✔ browser application bundle generation complete. ✔ Copying assets complete. ✔ Index html generation complete. Initial Chunk Files | Names | Size main.0deeec3d31917b111ae5.js | main | 104.77 kB polyfills.313e64a67eb403254da5.js | polyfills | 35.96 kB runtime.284ffbeed756735f6289.js | runtime | 1.02 kB styles.e5294562d7aae78a8725.css | styles | 113 bytes | Initial Total | 141.86 kB

      Notice how the file names have a unique hash after their name. Angular adds this to allow for “cache-busting”. As the hash changes on each build, browsers can reset their cache when they revisit the site.

      If you make any changes to the file, the hash will change, and the browser will reload the new file. Otherwise, the browser will use the cached version.

      The Angular CLI can also show warnings at this stage when the bundle size is too large. You should continue to review bundle sizes as your application grows.

      You can find the configuration options that correspond to the Angular CLI commands, such as ng build and ng serve, in the angular.json file. Navigate through the configurations key to view the options set for the production and development builds. You can configure these limits on a per-build basis.

      angular.json

      {
       ...
       "projects": {
       "my-first-angular-app": {
       "architect": {
       "build": {
       ...
       "configurations": {
       "production": {
       "budgets": [
       {
        "type": "initial",
        "maximumWarning": "500kb",
        "maximumError": "1mb"
       },
       {
        "type": "anyComponentStyle",
        "maximumWarning": "2kb",
        "maximumError": "4kb"
       }
      ],
                  }
                }
              }
            }
          }
        }
      }
      

      The ng build command outputs these to the dist/my-first-angular-app folder to access the built files for deployment. You can use these files to deploy the application to a server or CDN. For more information on this, see the Angular CLI documentation.

      In this step, you learned how to use the Angular CLI to create a development and production build. You also considered important factors that go into producing a performant web application, such as bundle size.

      Conclusion

      In this tutorial, you created an Angular project using the Angular CLI. You excecuted and ran the project in the browser, and updated the way the application looks and performs.

      You used the CLI to generate a production build and learned about configuration options for a deeper insight into how and where the CLI operates.

      To learn more about the Angular CLI and additional commands, check out the Angular CLI documentation.



      Source link