使用 Azure Cosmos DB 的用于 MongoDB 的 API 创建 Angular 应用 - 使用 Angular 生成 UICreate an Angular app with Azure Cosmos DB's API for MongoDB - Build the UI with Angular

本教程包含多个部分,演示了如何通过 Express 和 Angular 创建以 Node.js 编写的新应用,然后将其连接到使用 Cosmos DB 的用于 MongoDB 的 API 配置的 Cosmos 帐户This multi-part tutorial demonstrates how to create a new app written in Node.js with Express and Angular and then connect it to your Cosmos account configured with Cosmos DB's API for MongoDB.

本教程的第 3 部分基于第 2 部分,涵盖以下任务:Part 3 of the tutorial builds on Part 2 and covers the following tasks:

  • 生成 Angular UIBuild the Angular UI
  • 使用 CSS 设置外观Use CSS to set the look and feel
  • 在本地测试应用Test the app locally

先决条件Prerequisites

开始教程的此部分之前,请确保已完成教程第 2 部分的步骤。Before starting this part of the tutorial, ensure you've completed the steps in Part 2 of the tutorial.

提示

本教程介绍生成应用程序的各个步骤。This tutorial walks you through the steps to build the application step-by-step. 若要下载完成的项目,可从 GitHub 上的 angular-cosmosdb 存储库获取完成的应用程序。If you want to download the finished project, you can get the completed application from the angular-cosmosdb repo on GitHub.

生成 UIBuild the UI

  1. 在 Visual Studio Code 中,单击“停止”按钮 以停止 Node 应用。

  2. 在 Windows 命令提示符或 Mac Terminal 窗口中输入以下命令,生成 heroes 组件。In your Windows Command Prompt or Mac Terminal window, enter the following command to generate a heroes component. 在此代码中,g=generate(生成),c=component(组件),heroes=组件名称,并且由于使用的是平面文件结构 (--flat),因此没有为其创建子文件夹。In this code g=generate, c=component, heroes=name of component, and it's using a flat file structure (--flat) so that a subfolder isn't created for it.

    ng g c heroes --flat 
    

    Terminal 窗口显示新组件的确认信息。The terminal window displays confirmation of the new components.

    安装 hero 组件

    请看看已创建和更新的文件。Let's take a look at the files that were created and updated.

  3. 在 Visual Studio Code 的“资源管理器”窗格中,导航到新的 src\app 文件夹并打开在应用文件夹内生成的新 heroes.component.ts 文件。In Visual Studio Code, in the Explorer pane, navigate to the new src\app folder and open the new heroes.component.ts file generated within the app folder. 此 TypeScript 组件文件是上一命令创建的。This TypeScript component file was created by the previous command.

    提示

    如果应用文件夹没有显示在 Visual Studio Code 中,请在 Mac 中输入 CMD + SHIFT P 或在 Windows 中输入 Ctrl + Shift + P 以打开命令面板,然后键入“Reload Window”以获取系统更改。If the app folder doesn't display in Visual Studio Code, enter CMD + SHIFT P on a Mac or Ctrl + Shift + P on Windows to open the Command Palette, and then type Reload Window to pick up the system change.

  4. 在同一文件夹中打开 app.module.ts 文件。请注意,该文件在第 5 行向声明添加了 HeroesComponent,并在第 10 行对其进行了导入操作。In the same folder, open the app.module.ts file, and notice that it added the HeroesComponent to the declarations on line 5 and it imported it as well on line 10.

    打开 app-module.ts 文件

  5. 返回到 heroes.component.html 文件,将此代码复制到其中。Go back to the heroes.component.html file and copy in this code. <div> 是整个页面的容器。The <div> is the container for the entire page. 容器内有一个需创建的 hero 的列表。创建这些组件后,在 UI 中单击其中一个组件即可将其选定进行编辑或删除操作。Inside of the container there is a list of heroes which we need to create so that when you click on one you can select it and edit it or delete it in the UI. 然后就会在 HTML 中显示某种样式,可以据此判断选择了哪个组件。Then in the HTML we've got some styling so you know which one has been selected. 此外还有编辑区域,用于添加新的 hero 或编辑现有的 hero。There's also an edit area so that you can add a new hero or edit an existing one.

    <div>
      <ul class="heroes">
        <li *ngFor="let hero of heroes" (click)="onSelect(hero)" [class.selected]="hero === selectedHero">
          <button class="delete-button" (click)="deleteHero(hero)">Delete</button>
          <div class="hero-element">
            <div class="badge">{{hero.id}}</div>
            <div class="name">{{hero.name}}</div>
            <div class="saying">{{hero.saying}}</div>
          </div>
        </li>
      </ul>
      <div class="editarea">
        <button (click)="enableAddMode()">Add New Hero</button>
        <div *ngIf="selectedHero">
          <div class="editfields">
            <div>
              <label>id: </label>
              <input [(ngModel)]="selectedHero.id" placeholder="id" *ngIf="addingHero" />
              <label *ngIf="!addingHero" class="value">{{selectedHero.id}}</label>
            </div>
            <div>
              <label>name: </label>
              <input [(ngModel)]="selectedHero.name" placeholder="name" />
            </div>
            <div>
              <label>saying: </label>
              <input [(ngModel)]="selectedHero.saying" placeholder="saying" />
            </div>
          </div>
          <button (click)="cancel()">Cancel</button>
          <button (click)="save()">Save</button>
        </div>
      </div>
    </div>
    
  6. 准备好 HTML 之后,需将其添加到 heroes.component.ts 文件,然后才能与模板交互。Now that we've got the HTML in place, we need to add it to the heroes.component.ts file so we can interact with the template. 以下代码将模板添加到组件文件中。The following code adds the template to your component file. 已添加了一个构造函数,该函数用于获取某些 hero 并初始化 hero 服务组件,以便获取所有数据。A constructor has been added that gets some heroes, and initializes the hero service component to go get all the data. 此代码还添加了所有必需的方法,用于在 UI 中处理事件。This code also adds all of the necessary methods for handling events in the UI. 可以复制以下代码,覆盖 heroes.component.ts 中的现有代码。You can copy the following code over the existing code in heroes.component.ts. 应在 Hero 和 HeroService 区域中发现错误,因为相应的组件尚未导入,将在下一部分中解决这些错误。It's expected to see errors at the Hero and HeroService areas as the corresponding components aren't imported yet, you will fix these error in the next section.

    import { Component, OnInit } from '@angular/core';
    
    @Component({
      selector: 'app-heroes',
      templateUrl: './heroes.component.html',
      styleUrls: ['./heroes.component.scss']
    })
    export class HeroesComponent implements OnInit {
      addingHero = false;
      heroes: any = [];
      selectedHero: Hero;
    
      constructor(private heroService: HeroService) {}
    
      ngOnInit() {
       this.getHeroes();
      }
    
      cancel() {
        this.addingHero = false;
        this.selectedHero = null;
      }
    
      deleteHero(hero: Hero) {
        this.heroService.deleteHero(hero).subscribe(res => {
          this.heroes = this.heroes.filter(h => h !== hero);
          if (this.selectedHero === hero) {
            this.selectedHero = null;
          }
        });
      }
    
      getHeroes() {
        return this.heroService.getHeroes().subscribe(heroes => {
          this.heroes = heroes;
        });
      }
    
      enableAddMode() {
        this.addingHero = true;
        this.selectedHero = new Hero();
      }
    
      onSelect(hero: Hero) {
        this.addingHero = false;
        this.selectedHero = hero;
      }
    
      save() {
        if (this.addingHero) {
          this.heroService.addHero(this.selectedHero).subscribe(hero => {
            this.addingHero = false;
            this.selectedHero = null;
            this.heroes.push(hero);
          });
        } else {
          this.heroService.updateHero(this.selectedHero).subscribe(hero => {
            this.addingHero = false;
            this.selectedHero = null;
          });
        }
      }
    }
    
  7. 在资源管理器中打开 app/app.module.ts 文件,更新“import”节以添加 FormsModule 的导入。In Explorer, open the app/app.module.ts file and update the imports section to add an import for a FormsModule. import 节现在应如下所示:The import section should now look as follows:

    imports: [
      BrowserModule,
      FormsModule
    ],
    
  8. app/app.module.ts 文件的第 3 行中,为新的 FormsModule 模块添加导入。In the app/app.module.ts file add an import for the new FormsModule module on line 3.

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

使用 CSS 设置外观Use CSS to set the look and feel

  1. 在“资源管理器”窗格中,打开 src/styles.scss 文件。In the Explorer pane, open the src/styles.scss file.

  2. 将以下代码复制到 styles.scss 文件中,替换文件的现有内容。Copy the following code into the styles.scss file, replacing the existing content of the file.

    /* You can add global styles to this file, and also import other style files */
    
    * {
      font-family: Arial;
    }
    h2 {
      color: #444;
      font-weight: lighter;
    }
    body {
      margin: 2em;
    }
    
    body,
    input[text],
    button {
      color: #888;
      // font-family: Cambria, Georgia;
    }
    button {
      font-size: 14px;
      font-family: Arial;
      background-color: #eee;
      border: none;
      padding: 5px 10px;
      border-radius: 4px;
      cursor: pointer;
      cursor: hand;
      &:hover {
        background-color: #cfd8dc;
      }
      &.delete-button {
        float: right;
        background-color: gray !important;
        background-color: rgb(216, 59, 1) !important;
        color: white;
        padding: 4px;
        position: relative;
        font-size: 12px;
      }
    }
    div {
      margin: .1em;
    }
    
    .selected {
      background-color: #cfd8dc !important;
      background-color: rgb(0, 120, 215) !important;
      color: white;
    }
    
    .heroes {
      float: left;
      margin: 0 0 2em 0;
      list-style-type: none;
      padding: 0;
      li {
        cursor: pointer;
        position: relative;
        left: 0;
        background-color: #eee;
        margin: .5em;
        padding: .5em;
        height: 3.0em;
        border-radius: 4px;
        width: 17em;
        &:hover {
          color: #607d8b;
          color: rgb(0, 120, 215);
          background-color: #ddd;
          left: .1em;
        }
        &.selected:hover {
          /*background-color: #BBD8DC !important;*/
          color: white;
        }
      }
      .text {
        position: relative;
        top: -3px;
      }
      .saying {
        margin: 5px 0;
      }
      .name {
        font-weight: bold;
      }
      .badge {
        /* display: inline-block; */
        float: left;
        font-size: small;
        color: white;
        padding: 0.7em 0.7em 0 0.5em;
        background-color: #607d8b;
        background-color: rgb(0, 120, 215);
        background-color:rgb(134, 183, 221);
        line-height: 1em;
        position: relative;
        left: -1px;
        top: -4px;
        height: 3.0em;
        margin-right: .8em;
        border-radius: 4px 0 0 4px;
        width: 1.2em;
      }
    }
    
    .header-bar {
      background-color: rgb(0, 120, 215);
      height: 4px;
      margin-top: 10px;
      margin-bottom: 10px;
    }
    
    label {
      display: inline-block;
      width: 4em;
      margin: .5em 0;
      color: #888;
      &.value {
        margin-left: 10px;
        font-size: 14px;
      }
    }
    
    input {
      height: 2em;
      font-size: 1em;
      padding-left: .4em;
      &::placeholder {
          color: lightgray;
          font-weight: normal;
          font-size: 12px;
          letter-spacing: 3px;
      }
    }
    
    .editarea {
      float: left;
      input {
        margin: 4px;
        height: 20px;
        color: rgb(0, 120, 215);
      }
      button {
        margin: 8px;
      }
      .editfields {
        margin-left: 12px;
      }
    }
    
  3. 保存文件。Save the file.

显示组件Display the component

有了组件以后,如何让其显示在平面上?Now that we have the component, how do we get it to show up on the screen? 请修改 app.component.ts 中的默认组件。Let's modify the default components in app.component.ts.

  1. 在“资源管理器”窗格中,打开 /app/app.component.ts,将标题更改为 Heroes,然后将已创建组件的名称置于 heroes.components.ts (app-heroes) 中,以便引用该新组件。In the Explorer pane, open /app/app.component.ts, change the title to Heroes, and then put the name of the component we created in heroes.components.ts (app-heroes) to refer to that new component. 文件的内容现在应如下所示:The contents of the file should now look as follows:

    import { Component } from '@angular/core';
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.scss'],
      template: `
      <h1>Heroes</h1>
      <div class="header-bar"></div>
      <app-heroes></app-heroes>
    `
    })
    export class AppComponent {
      title = 'app';
    }
    
    
  2. heroes.components.ts 中有其他需要引用的组件(类似于 Hero 组件),因此还需创建此类组件。There are other components in heroes.components.ts that we're referring to, like the Hero component, so we need to go create that, too. 在 Angular CLI 命令提示符中,使用以下命令创建一个 hero 模型和一个名为 hero.ts 的文件,其中,g=generate(生成),cl=class(类),hero=类名。In the Angular CLI command prompt, use the following command to create a hero model and a file named hero.ts, where g=generate, cl=class, and hero=name of class.

    ng g cl hero
    
  3. 在“资源管理器”窗格中,打开 src\app\hero.ts。In the Explorer pane, open src\app\hero.ts. 在 hero.ts 中,将文件的内容替换为以下代码,以便添加带 ID、name 和 saying 的 Hero 类。In hero.ts, replace the content of the file with the following code, which adds a Hero class with an ID, a name, and a saying.

      export class Hero {
      id: number;
      name: string;
      saying: string;
    }
    
  4. 返回到 heroes.components.ts。请注意,selectedHero: Hero; 行(第 10 行)的 Hero 下方有一条红线。Go back to heroes.components.ts and notice that on the selectedHero: Hero; line (line 10), Hero has a red line underneath.

  5. 左键单击 Hero 一词,Visual Studio 会在代码块左侧显示一个灯泡图标。Left-click the term Hero, and Visual Studio displays a lightbulb icon on the left side of the code block.

    Visual Studio Code 中的灯泡

  6. 单击灯泡,然后单击“从 ‘/app/hero’ 导入 Hero”。Click the lightbulb and then click Import Hero from "/app/hero". 或“从 "./hero" 导入 Hero”。or Import Hero from "./hero". (此消息因设置而异)(The message changes depending on your setup)

    此时会在第 2 行出现新的代码行。A new line of code appears on line 2. 如果第 2 行引用 /app/hero,请对其进行修改,使之引用本地文件夹中的 hero 文件 (./hero)。If line 2 references /app/hero, modify it so that it references the hero file from the local folder (./hero). 第 2 行应如下所示:Line 2 should look like this:

    import { Hero } from "./hero";
    

    该代码适用于模型,而我们仍需创建服务。That takes care of the model, but we still need to create the service.

创建服务Create the Service

  1. 在 Angular CLI 命令提示符中输入以下命令,以便在 app.module.ts 中创建 hero 服务,其中,g=generate(生成),s=service(服务),hero=服务名称,-m=置于 app.module 中。In the Angular CLI command prompt, enter the following command to create a hero service in app.module.ts, where g=generate, s=service, hero=name of service, -m=put in app.module.

    ng g s hero -m app.module
    
  2. 在 Visual Studio Code 中返回到 heroes.components.ts。In Visual Studio Code, go back to heroes.components.ts. 请注意,constructor(private heroService: HeroService) {} 行(第 13 行)的 HeroService 下方有一条红线。Notice that on the constructor(private heroService: HeroService) {} line (line 13), HeroService has a red line underneath. 单击 HeroService,此时会在代码块左侧出现灯泡。Click HeroService, and you'll get the lightbulb on the left side of code block. 单击该灯泡,然后单击“从 "./hero.service" 导入 HeroService”Click the light bulb and then click Import HeroService from "./hero.service ". 或“从 ‘/app/hero.service’ 导入 HeroService”。or Import HeroService from "/app/hero.service ".

    单击灯泡会在第 2 行中插入新的代码行。Clicking the light bulb inserts a new line of code on line 2. 如果第 2 行引用 /app/hero.service 文件夹,请对其进行修改,使之引用本地文件夹中的 hero 文件 (./hero.service)。If line 2 references the /app/hero.service folder, modify it so that it references the hero file from the local folder (./hero.service). 第 2 行应如下所示:Line 2 should look like this:

    import { HeroService } from "./hero.service"
    
  3. 在 Visual Studio Code 中打开 hero.service.ts,将以下代码复制到其中,替换文件的内容。In Visual Studio Code, open hero.service.ts and copy in the following code, replacing the content of the file.

    import { Injectable } from '@angular/core';
    import { HttpClient } from '@angular/common/http';
    
    import { Hero } from './hero';
    
    const api = '/api';
    
    @Injectable()
    export class HeroService {
      constructor(private http: HttpClient) {}
    
      getHeroes() {
        return this.http.get<Array<Hero>>(`${api}/heroes`)
      }
    
      deleteHero(hero: Hero) {
        return this.http.delete(`${api}/hero/${hero.id}`);
      }
    
      addHero(hero: Hero) {
        return this.http.post<Hero>(`${api}/hero/`, hero);
      }
    
      updateHero(hero: Hero) {
        return this.http.put<Hero>(`${api}/hero/${hero.id}`, hero);
      }
    }
    

    此代码使用 Angular 提供的最新版 HttpClient,这是你需要提供的一个模块,因此我们接下来会进行相应的操作。This code uses the newest version of the HttpClient that Angular offers, which is a module that you need to provide, so we'll do that next.

  4. 在 Visual Studio Code 中打开 app.module.ts 并更新 import 节,使之包括 HttpClientModule,从而导入 HttpClientModule。In Visual Studio Code, open app.module.ts and import the HttpClientModule by updating the import section to include HttpClientModule.

    imports: [
      BrowserModule,
      FormsModule,
      HttpClientModule
    ],
    
  5. 在 app.module.ts 中,从 import 列表添加 HttpClientModule import 语句。In app.module.ts, add the HttpClientModule import statement the list of imports.

    import { HttpClientModule } from '@angular/common/http';
    
  6. 在 Visual Studio Code 中保存所有文件。Save all files in Visual Studio Code.

生成应用Build the app

  1. 在命令提示符处输入以下命令,生成 Angular 应用程序。At the command prompt, enter the following command to build the Angular application.

    ng b
    

    如果存在问题,Terminal 窗口会显示要修改的文件的相关信息。If there are any problems, the terminal window displays information about the files to fix. 生成操作完成后,新文件会进入 dist 文件夹中。When the build completes, the new files go into the dist folder. 可以根据需要查看 dist 文件夹中的新文件。You can review the files new in the dist folder if you want.

    现在运行该应用。Now let's run the app.

  2. 在 Visual Studio Code 中,单击左侧的“调试”按钮 ,然后单击“启动调试”按钮

  3. 现在请打开 Internet 浏览器,导航到 localhost:3000,然后就会看到该应用在本地运行。Now open an internet browser and navigate to localhost:3000 and see the app running locally.

    在本地运行的 Hero 应用程序

后续步骤Next steps

在本教程的此部分,你已完成以下操作:In this part of the tutorial, you've done the following:

  • 生成 Angular UIBuilt the Angular UI
  • 在本地测试应用Tested the app locally

你可以转到本教程的下一部分,了解如何创建 Azure Cosmos DB 帐户。You can proceed to the next part of the tutorial to create an Azure Cosmos DB account.