MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

创建你的第一个Angular项目

2024-02-162.6k 阅读

环境准备

在开始创建 Angular 项目之前,确保你已经安装了以下工具:

  1. Node.js:Angular 项目依赖于 Node.js 运行时环境,你可以从Node.js 官方网站下载并安装最新的 LTS(长期支持)版本。安装完成后,在命令行中输入 node -vnpm -v 来验证安装是否成功,它们会分别输出版本号。
  2. Angular CLI:Angular CLI(命令行界面)是用于初始化、开发和维护 Angular 项目的核心工具。通过 npm(Node 包管理器,随 Node.js 一同安装)全局安装 Angular CLI,在命令行中执行以下命令:
npm install -g @angular/cli

安装完成后,输入 ng --version 验证 Angular CLI 是否安装成功并查看其版本号。

创建新项目

一切准备就绪后,就可以开始创建第一个 Angular 项目了。打开命令行终端,导航到你想要创建项目的目录,例如:

cd C:\projects

然后,使用 Angular CLI 创建一个新的 Angular 项目,执行以下命令:

ng new my - first - angular - project

这里,my - first - angular - project 是项目的名称,你可以根据自己的喜好替换。在创建过程中,Angular CLI 会询问你一些问题:

  1. 是否使用 Angular routing:路由用于在单页应用中实现页面导航。如果你的应用需要多个页面之间进行导航,输入 y;如果是简单的单页面应用,输入 n。例如,对于一个简单的展示型应用,可能不需要路由,此时输入 n
  2. 选择 CSS 预处理器:Angular CLI 支持多种 CSS 预处理器,如 Sass、Less 和 Stylus。如果你对预处理器不熟悉,选择默认的 CSS 即可。如果选择 Sass,它会在项目中配置 Sass 相关的设置,方便你使用 Sass 的特性,如变量、嵌套等。

项目结构剖析

创建好项目后,进入项目目录:

cd my - first - angular - project

使用你喜欢的代码编辑器(如 Visual Studio Code)打开项目,此时可以看到项目的目录结构,以下是一些主要目录和文件的介绍:

  1. src 目录:这是项目的核心源代码目录,所有与应用相关的代码都在这里编写。
    • app 目录:存放应用的组件、服务、模块等核心代码。一个典型的 Angular 应用由多个组件组成,每个组件都有自己的逻辑和视图。
    • assets 目录:用于存放静态资源,如图像、字体、配置文件等。例如,你可以将项目中使用的 logo 图片放在这个目录下,然后在组件中通过相对路径引用。
    • environments 目录:包含不同环境的配置文件,如 environment.ts 用于开发环境,environment.prod.ts 用于生产环境。这些文件可以用于配置 API 端点等在不同环境下可能变化的参数。
  2. angular.json 文件:这是项目的配置文件,包含了项目的构建、测试、部署等各种配置选项。例如,你可以在这里配置项目的输出路径、启用或禁用特定的构建优化等。
  3. package.json 文件:记录了项目的依赖项以及一些脚本命令。当你使用 npm install 安装新的包时,相关信息会自动更新到这个文件中。同时,你可以在 scripts 字段中定义自定义的脚本命令,方便执行一些特定的任务。

运行项目

在项目目录下,通过以下命令启动 Angular 开发服务器:

ng serve

该命令会编译项目并启动一个本地服务器,默认情况下,应用会在 http://localhost:4200 上运行。你可以在浏览器中打开这个地址,就能看到默认的 Angular 欢迎页面。在开发过程中,Angular CLI 会监控文件的变化,当你修改了源代码,服务器会自动重新编译并刷新页面,方便你实时查看更改效果。

创建第一个组件

组件是 Angular 应用的基本构建块,它包含了视图(HTML)、逻辑(TypeScript)和样式(CSS)。使用 Angular CLI 创建一个新的组件非常简单,在项目目录下执行以下命令:

ng generate component my - first - component

这会在 src/app 目录下创建一个名为 my - first - component 的新组件,同时生成以下文件:

  1. my - first - component.component.ts:这是组件的逻辑文件,使用 TypeScript 编写。以下是一个简单的示例:
import { Component } from '@angular/core';

@Component({
  selector: 'app - my - first - component',
  templateUrl: './my - first - component.component.html',
  styleUrls: ['./my - first - component.component.css']
})
export class MyFirstComponentComponent {
  message: string = 'Hello, Angular!';
}

在这个组件类中,我们定义了一个 message 属性,它是一个字符串类型,值为 Hello, Angular!@Component 装饰器用于配置组件,selector 定义了组件在 HTML 中的标签名,templateUrl 指向组件的视图模板文件,styleUrls 指向组件的样式文件。 2. my - first - component.component.html:这是组件的视图模板文件,使用 HTML 编写。在这个文件中,我们可以使用 Angular 的模板语法来展示数据和定义交互。例如:

<div>
  <h1>{{message}}</h1>
</div>

这里使用了双花括号 {{}} 来插值表达式,将组件类中的 message 属性的值显示在页面上。 3. my - first - component.component.css:这是组件的样式文件,用于定义组件的样式。例如,我们可以为上面的 h1 标签添加一些样式:

h1 {
  color: blue;
}

在应用中使用组件

要在整个应用中使用我们刚刚创建的组件,需要将其添加到应用的主组件模板中。打开 src/app/app.component.html 文件,将以下代码添加进去:

<app - my - first - component></app - my - first - component>

保存文件后,刷新浏览器,你会看到页面上显示出蓝色的 Hello, Angular!,这表明我们的组件已经成功在应用中使用。

数据绑定

数据绑定是 Angular 中的一个重要概念,它允许在组件的视图和逻辑之间建立联系。Angular 支持以下几种数据绑定方式:

  1. 插值绑定:如前面在 my - first - component.component.html 中使用的 {{message}},它将组件类中的属性值插入到视图中。
  2. 属性绑定:用于设置 HTML 元素的属性值。例如,在 my - first - component.component.html 中添加一个图片标签,并通过属性绑定设置其 src 属性:
<div>
  <h1>{{message}}</h1>
  <img [src]="imageUrl" alt="My Image">
</div>

然后在 my - first - component.component.ts 中添加 imageUrl 属性:

import { Component } from '@angular/core';

@Component({
  selector: 'app - my - first - component',
  templateUrl: './my - first - component.component.html',
  styleUrls: ['./my - first - component.component.css']
})
export class MyFirstComponentComponent {
  message: string = 'Hello, Angular!';
  imageUrl: string = 'assets/logo.png';
}

这样,图片就会根据 imageUrl 的值进行显示。这里的 [src] 就是属性绑定的语法,方括号将属性名括起来。 3. 事件绑定:用于监听 HTML 元素的事件。例如,添加一个按钮,并监听其点击事件:

<div>
  <h1>{{message}}</h1>
  <img [src]="imageUrl" alt="My Image">
  <button (click)="onButtonClick()">Click Me</button>
</div>

my - first - component.component.ts 中添加 onButtonClick 方法:

import { Component } from '@angular/core';

@Component({
  selector: 'app - my - first - component',
  templateUrl: './my - first - component.component.html',
  styleUrls: ['./my - first - component.component.css']
})
export class MyFirstComponentComponent {
  message: string = 'Hello, Angular!';
  imageUrl: string = 'assets/logo.png';

  onButtonClick() {
    this.message = 'Button Clicked!';
  }
}

当点击按钮时,onButtonClick 方法会被调用,从而更新 message 属性的值,视图也会随之更新。这里的 (click) 就是事件绑定的语法,圆括号将事件名括起来。 4. 双向数据绑定:在表单元素中经常使用,它结合了属性绑定和事件绑定,使得视图和模型之间的数据能够实时同步。例如,创建一个输入框,并实现双向数据绑定: 首先,在 my - first - component.component.html 中添加输入框:

<div>
  <h1>{{message}}</h1>
  <img [src]="imageUrl" alt="My Image">
  <button (click)="onButtonClick()">Click Me</button>
  <input [(ngModel)]="message" type="text">
</div>

然后,需要在 my - first - component.component.ts 所在的模块中导入 FormsModule。打开 src/app/app.module.ts 文件,添加以下导入和配置:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform - browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { MyFirstComponentComponent } from './my - first - component/my - first - component.component';

@NgModule({
  declarations: [
    AppComponent,
    MyFirstComponentComponent
  ],
  imports: [
    BrowserModule,
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}

这样,当在输入框中输入内容时,message 属性的值会实时更新,同时视图上显示的 message 也会实时变化。这里的 [(ngModel)] 就是双向数据绑定的语法,它是 [ngModel](属性绑定)和 (ngModelChange)(事件绑定)的组合。

组件交互

在一个复杂的 Angular 应用中,组件之间通常需要进行交互。以下是几种常见的组件交互方式:

  1. 父子组件交互
    • 父组件向子组件传递数据:通过输入属性实现。首先,在子组件 my - first - component.component.ts 中定义一个输入属性。修改 my - first - component.component.ts 如下:
import { Component, Input } from '@angular/core';

@Component({
  selector: 'app - my - first - component',
  templateUrl: './my - first - component.component.html',
  styleUrls: ['./my - first - component.component.css']
})
export class MyFirstComponentComponent {
  @Input() parentData: string;
}

然后在父组件 app.component.html 中使用子组件并传递数据:

<app - my - first - component [parentData]="parentMessage"></app - my - first - component>

app.component.ts 中定义 parentMessage 属性:

import { Component } from '@angular/core';

@Component({
  selector: 'app - root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  parentMessage: string = 'Data from parent';
}

在子组件的视图 my - first - component.component.html 中可以显示这个数据:

<div>
  <p>{{parentData}}</p>
</div>
- **子组件向父组件传递数据**:通过输出属性和事件发射器实现。在子组件 `my - first - component.component.ts` 中定义一个输出属性和一个方法来触发事件:
import { Component, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app - my - first - component',
  templateUrl: './my - first - component.component.html',
  styleUrls: ['./my - first - component.component.css']
})
export class MyFirstComponentComponent {
  @Output() childEvent = new EventEmitter<string>();

  sendDataToParent() {
    this.childEvent.emit('Data from child');
  }
}

在子组件的视图 my - first - component.component.html 中添加一个按钮来触发这个方法:

<div>
  <button (click)="sendDataToParent()">Send Data to Parent</button>
</div>

在父组件 app.component.html 中监听子组件的事件:

<app - my - first - component (childEvent)="handleChildEvent($event)"></app - my - first - component>

app.component.ts 中定义 handleChildEvent 方法来处理接收到的数据:

import { Component } from '@angular/core';

@Component({
  selector: 'app - root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  handleChildEvent(data: string) {
    console.log('Received data from child:', data);
  }
}
  1. 兄弟组件交互:通常通过一个共享服务来实现。首先,使用 Angular CLI 创建一个共享服务:
ng generate service shared

这会在 src/app 目录下生成 shared.service.ts 文件。修改 shared.service.ts 如下:

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class SharedService {
  private messageSource = new Subject<string>();
  message$ = this.messageSource.asObservable();

  sendMessage(message: string) {
    this.messageSource.next(message);
  }
}

这个服务使用 RxJS 的 Subject 来创建一个可观察对象 message$,并提供一个 sendMessage 方法来发送消息。

然后,在其中一个兄弟组件(假设为 brother - component - 1.component.ts)中注入服务并发送消息:

import { Component } from '@angular/core';
import { SharedService } from '../shared.service';

@Component({
  selector: 'app - brother - component - 1',
  templateUrl: './brother - component - 1.component.html',
  styleUrls: ['./brother - component - 1.component.css']
})
export class BrotherComponent1Component {
  constructor(private sharedService: SharedService) {}

  sendMessage() {
    this.sharedService.sendMessage('Message from brother 1');
  }
}

在其视图 brother - component - 1.component.html 中添加一个按钮来触发发送消息的方法:

<div>
  <button (click)="sendMessage()">Send Message to Brother 2</button>
</div>

在另一个兄弟组件 brother - component - 2.component.ts 中注入服务并订阅消息:

import { Component } from '@angular/core';
import { SharedService } from '../shared.service';

@Component({
  selector: 'app - brother - component - 2',
  templateUrl: './brother - component - 2.component.html',
  styleUrls: ['./brother - component - 2.component.css']
})
export class BrotherComponent2Component {
  receivedMessage: string;

  constructor(private sharedService: SharedService) {
    this.sharedService.message$.subscribe(message => {
      this.receivedMessage = message;
    });
  }
}

在其视图 brother - component - 2.component.html 中显示接收到的消息:

<div>
  <p>Received message: {{receivedMessage}}</p>
</div>

模块

模块是 Angular 应用中的一个重要概念,它将相关的组件、服务、指令等组织在一起,提高代码的可维护性和复用性。Angular 应用至少有一个根模块,通常是 AppModule。在 src/app/app.module.ts 文件中,定义了 AppModule

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform - browser';
import { AppComponent } from './app.component';
import { MyFirstComponentComponent } from './my - first - component/my - first - component.component';

@NgModule({
  declarations: [
    AppComponent,
    MyFirstComponentComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}
  1. declarations:用于声明本模块中拥有的视图类,包括组件、指令和管道。例如,我们将 AppComponentMyFirstComponentComponent 声明在这里。
  2. imports:用于导入本模块需要使用的其他模块。BrowserModule 是 Angular 应用在浏览器环境中运行所需的基础模块。
  3. providers:用于注册服务,这些服务可以在整个应用中使用。如果一个服务在这里注册,它会被创建为单例,在应用的任何地方注入时都是同一个实例。
  4. bootstrap:指定应用的主组件,这里是 AppComponent。当应用启动时,Angular 会首先渲染这个组件。

在大型应用中,通常会创建多个特性模块来组织不同功能的代码。例如,创建一个用户模块 UserModule 来管理与用户相关的组件、服务等。首先,使用 Angular CLI 创建模块:

ng generate module user

这会在 src/app 目录下生成 user.module.ts 文件。假设我们有一个 UserComponent 用于显示用户信息,在 user.module.ts 中配置如下:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { UserComponent } from './user.component';

@NgModule({
  declarations: [
    UserComponent
  ],
  imports: [
    CommonModule
  ],
  providers: [],
  exports: [UserComponent]
})
export class UserModule {}

这里导入了 CommonModule,它包含了一些常用的指令和管道,如 NgIfNgFor 等。exports 数组用于指定哪些组件、指令或管道可以被其他模块使用。如果其他模块导入了 UserModule,就可以使用 UserComponent

然后,在 AppModule 中导入 UserModule

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform - browser';
import { AppComponent } from './app.component';
import { MyFirstComponentComponent } from './my - first - component/my - first - component.component';
import { UserModule } from './user.module';

@NgModule({
  declarations: [
    AppComponent,
    MyFirstComponentComponent
  ],
  imports: [
    BrowserModule,
    UserModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}

这样,就可以在 AppComponent 的视图中使用 UserComponent 了。

服务

服务是在 Angular 应用中用于处理一些通用逻辑的类,如数据获取、日志记录等。以数据获取为例,假设我们有一个 API 用于获取用户列表,创建一个服务来处理这个逻辑。首先,使用 Angular CLI 创建服务:

ng generate service user - data

这会在 src/app 目录下生成 user - data.service.ts 文件。修改 user - data.service.ts 如下:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class UserDataService {
  private apiUrl = 'https://example.com/api/users';

  constructor(private http: HttpClient) {}

  getUsers(): Observable<any[]> {
    return this.http.get<any[]>(this.apiUrl);
  }
}

这里注入了 HttpClient,它是 Angular 提供的用于处理 HTTP 请求的服务。getUsers 方法返回一个 Observable,表示异步操作的结果,通过 http.get 方法从指定的 API 地址获取用户数据。

在组件中使用这个服务,例如在 UserComponent 中:

import { Component } from '@angular/core';
import { UserDataService } from '../user - data.service';

@Component({
  selector: 'app - user - component',
  templateUrl: './user - component.component.html',
  styleUrls: ['./user - component.component.css']
})
export class UserComponent {
  users: any[];

  constructor(private userDataService: UserDataService) {}

  ngOnInit() {
    this.userDataService.getUsers().subscribe(data => {
      this.users = data;
    });
  }
}

ngOnInit 生命周期钩子函数中,调用服务的 getUsers 方法,并通过 subscribe 方法处理获取到的数据,将其赋值给 users 属性,然后可以在视图中显示用户列表。

路由

路由允许在单页应用中实现页面导航,提供类似于多页应用的用户体验。假设我们的应用有两个页面,一个是首页,一个是关于页面。首先,在创建项目时如果选择了使用路由,Angular CLI 已经为我们生成了基本的路由配置文件 app - routing.module.ts。如果没有选择,可以通过以下命令生成:

ng generate module app - routing --flat --module=app

--flat 选项表示将文件放在 src/app 目录下而不是创建一个新的 app - routing 目录,--module=app 表示将这个模块注册到 AppModule 中。

打开 app - routing.module.ts 文件,配置如下:

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

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

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

这里定义了两条路由,一条是空路径,对应首页组件 HomeComponent;另一条是 about 路径,对应关于页面组件 AboutComponent

然后创建 HomeComponentAboutComponent

ng generate component home
ng generate component about

HomeComponentAboutComponent 的视图中添加一些内容,例如在 home.component.html 中:

<div>
  <h1>Home Page</h1>
</div>

about.component.html 中:

<div>
  <h1>About Page</h1>
</div>

app.component.html 中添加导航链接和路由出口:

<ul>
  <li><a routerLink="">Home</a></li>
  <li><a routerLink="about">About</a></li>
</ul>
<router - outlet></router - outlet>

routerLink 指令用于指定导航链接的路径,router - outlet 是路由出口,用于显示匹配路由的组件。

这样,当用户点击导航链接时,相应的组件会在路由出口中显示,实现页面导航功能。

总结与实践

通过以上步骤,我们完成了从创建 Angular 项目到构建一个具有基本功能的应用的过程,包括组件创建、数据绑定、组件交互、模块和服务的使用以及路由配置。在实际开发中,你可以根据项目需求进一步扩展和完善应用,例如添加更多的组件和功能,优化样式,处理更复杂的数据交互等。不断实践和学习,你将能够熟练掌握 Angular 开发技术,构建出高质量的前端应用。同时,要关注 Angular 的官方文档和社区资源,以获取最新的技术信息和最佳实践。