Project Icon

ng-demo

Angular教程 搭建搜索编辑应用

本教程详细介绍如何使用Angular和Angular CLI创建一个具有搜索和编辑功能的应用。内容包括项目初始化、搜索功能实现、后端服务集成、结果展示以及编辑功能添加。教程采用循序渐进的方式,展示了如何运用Angular CLI、TypeScript等工具开发完整的Angular应用,适合Angular入门学习。

= 🦴 Bare Bones Angular and Angular CLI Tutorial

:author: Matt Raible :email: matt@raibledesigns.com :revnumber: 17.0.0 :revdate: {docdate} :subject: Angular and Angular CLI :keywords: Angular, Angular CLI, TypeScript, JavaScript, Node, npm, Jasmine, Cypress :icons: font :lang: en :language: javadocript :sourcedir: . ifndef::env-github[] :icons: font endif::[] ifdef::env-github,env-browser[] :toc: preamble :toclevels: 2 endif::[] ifdef::env-github[] :status: :outfilesuffix: .adoc :!toc-title: :caution-caption: :fire: :important-caption: :exclamation: :note-caption: :paperclip: :tip-caption: :bulb: :warning-caption: :warning: endif::[] :toc: macro :source-highlighter: highlight.js

IMPORTANT: For a book of this tutorial, please check out https://www.infoq.com/minibooks/angular-mini-book/[The Angular Mini-Book]. Its "Build an Angular App" chapter was inspired by this tutorial.

This tutorial shows you how to build a bare-bones search and edit application using https://angular.io[Angular] and https://github.com/angular/angular-cli[Angular CLI] version 17.

toc::[]

ifdef::env-github[] TIP: It appears you're reading this document on GitHub. If you want a prettier view, install https://chrome.google.com/webstore/detail/asciidoctorjs-live-previe/iaalpfgpbocpdfblpnhhgllgbdbchmia[Asciidoctor.js Live Preview for Chrome], then view the https://raw.githubusercontent.com/mraible/ng-demo/main/README.adoc?toc=left[raw document]. Another option is to use the https://gist.asciidoctor.org/?github-mraible%2Fng-demo%2Fmain%2F%2FREADME.adoc[DocGist view]. endif::[]

.Source Code


If you'd like to get right to it, the https://github.com/mraible/ng-demo[source is on GitHub]. To run the app, use ng serve. To test it, run ng test. To run its integration tests, run ng e2e.


Check out the bonus section at the end of this document for Angular Material, Bootstrap, Auth0, and Electron tutorials.

toc::[]

== What you'll build

You'll build a simple web application with Angular CLI, a tool for Angular development. You'll create an application with search and edit features.

== What you'll need

If you don't have Angular CLI installed, install it:


npm install -g @angular/cli@17

NOTE: IntelliJ IDEA Ultimate Edition has the best support for TypeScript. If you'd rather not pay for your IDE, checkout https://code.visualstudio.com/[Visual Studio Code].

== Create a new Angular project

Create a new project using the ng new command:


ng new ng-demo

When prompted for the stylesheet format, choose "CSS" (the default). Accept the default (No) for SSR (Server-Side Rendering) and SSG (Static Site Generation).

This will create a ng-demo project and run npm install in it. It takes about a minute to complete, but will vary based on your internet connection speed.

You can see the version of Angular CLI you're using with the ng version command.


$ ng version

Angular CLI: 17.0.5 Node: 18.18.2 Package Manager: npm 9.8.1 OS: darwin arm64

Angular: ...

Package Version

@angular-devkit/architect 0.1700.5 (cli-only) @angular-devkit/core 17.0.5 (cli-only) @angular-devkit/schematics 17.0.5 (cli-only) @schematics/angular 17.0.5 (cli-only)

If you run this command from the ng-demo directory, you'll see even more information.


...

Angular: 17.0.5 ... animations, cli, common, compiler, compiler-cli, core, forms ... platform-browser, platform-browser-dynamic, router

Package Version

@angular-devkit/architect 0.1700.5 @angular-devkit/build-angular 17.0.5 @angular-devkit/core 17.0.5 @angular-devkit/schematics 17.0.5 @schematics/angular 17.0.5 rxjs 7.8.1 typescript 5.2.2 zone.js 0.14.2

== Run the application

The project is configured with a simple web server for development. To start it, run:


ng serve

You should see a screen like the one below at http://localhost:4200.

[[default-homepage]] .Default homepage image::src/assets/images/default-homepage.png[Default Homepage, 800, scaledwidth="100%"]

You can make sure your new project's tests pass, run ng test:


$ ng test ... ...: Executed 3 of 3 SUCCESS (0.048 secs / 0.044 secs)

== Add a search feature

To add a search feature, open the project in an IDE or your favorite text editor.

=== The Basics

In a terminal window, cd into the ng-demo directory and run the following command to create a search component.

[source]

ng g component search

Open src/app/search/search.component.html and replace its default HTML with the following:

[source,html] .src/app/search/search.component.html

Search

{{searchResults | json}}
----

Add a query property to src/app/search/search.component.ts. While you're there, add a searchResults property and an empty search() method.

[source,typescript] .src/app/search/search.component.ts

export class SearchComponent implements OnInit { query: string | undefined; searchResults: any;

constructor() { }

ngOnInit(): void { }

search(): void { }

}

In src/app/app.routes.ts, modify the routes constant to add SearchComponent as the default:

[source,typescript] .src/app/app.routes.ts

import { Routes } from '@angular/router'; import { SearchComponent } from './search/search.component';

export const routes: Routes = [ { path: 'search', component: SearchComponent }, { path: '', redirectTo: '/search', pathMatch: 'full' } ];

Run ng serve again you will see a compilation error.


⠹ Building...✘ [ERROR] NG8002: Can't bind to 'ngModel' since it isn't a known property of 'input'. [plugin angular-compiler]

To solve this, open search.component.ts. Import FormsModule and JsonPipe:

[source,typescript] .src/app/search/search.component.ts

import { Component, OnInit } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { JsonPipe } from '@angular/common';

@Component({ selector: 'app-search', standalone: true, imports: [FormsModule, JsonPipe], templateUrl: './search.component.html', styleUrl: './search.component.css' })

Now you should be able to see the search form.

[[search-component]] .Search component image::src/assets/images/search-without-css.png[Search component, 800, scaledwidth="100%"]

If yours looks different, it's because I trimmed my app.component.html to the bare minimum.

[source,html] .src/app/app.component.html

Welcome to {{ title }}!

If you want to add styling for this component, open src/app/search/search.component.css and add some CSS. For example:

[source,css] .src/app/search/search.component.css

:host { display: block; padding: 0 20px; }

IMPORTANT: The :host allows you to target the container of the component. It's the only way to target the host element. You can't reach the host element from inside the component with other selectors because it's not part of the component's own template.

This section has shown you how to generate a new component and add it to a basic Angular application with Angular CLI. The next section shows you how to create and use a JSON file and localStorage to create a fake API.

=== The Backend

To get search results, create a SearchService that makes HTTP requests to a JSON file. Start by generating a new service.


ng g service shared/search/search

Create src/assets/data/people.json to hold your data.


mkdir -p src/assets/data

[source,json] .src/assets/data/people.json

[ { "id": 1, "name": "Nikola Jokić", "phone": "(720) 555-1212", "address": { "street": "2000 16th Street", "city": "Denver", "state": "CO", "zip": "80202" } }, { "id": 2, "name": "Jamal Murray", "phone": "(303) 321-8765", "address": { "street": "2654 Washington Street", "city": "Lakewood", "state": "CO", "zip": "80568" } }, { "id": 3, "name": "Aaron Gordon", "phone": "(303) 323-1233", "address": { "street": "46 Creekside Way", "city": "Winter Park", "state": "CO", "zip": "80482" } } ]

Modify src/app/shared/search/search.service.ts and provide HttpClient as a dependency in its constructor.

In this same file, create a getAll() method to gather all the people. Also, define the Address and Person classes that JSON will be marshalled to.

[source,typescript] .src/app/shared/search/search.service.ts

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

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

constructor(private http: HttpClient) { }

getAll(): Observable<Person[]> { return this.http.get<Person[]>('assets/data/people.json'); } }

export class Address { street: string; city: string; state: string; zip: string;

constructor(obj?: any) { this.street = obj?.street || null; this.city = obj?.city || null; this.state = obj?.state || null; this.zip = obj?.zip || null; } }

export class Person { id: number; name: string; phone: string; address: Address;

constructor(obj?: any) { this.id = obj?.id || null; this.name = obj?.name || null; this.phone = obj?.phone || null; this.address = obj?.address || null; } }

To make these classes easier to consume by your components, create src/app/shared/index.ts and add the following:

[source,typescript] .src/app/shared/index.ts

export * from './search/search.service';

The reason for creating this file is so you can import multiple classes on a single line rather than having to import each individual class on separate lines.

In search.component.ts, add imports for these classes.

[source,typescript] .src/app/search/search.component.ts

import { Person, SearchService } from '../shared';

You can now add a proper type to the searchResults variable. While you're there, modify the constructor to inject the SearchService.

[source,typescript] .src/app/search/search.component.ts

export class SearchComponent implements OnInit { query: string | undefined; searchResults: Person[] = [];

constructor(private searchService: SearchService) { }

Then update the search() method to call the service's getAll() method.

[source,typescript] .src/app/search/search.component.ts

search(): void { this.searchService.getAll().subscribe({ next: (data: Person[]) => { this.searchResults = data; }, error: error => console.log(error) }); }

At this point, if your app is running, you'll see the following message in your browser's console.


NullInjectorError: No provider for _HttpClient!

To fix the "No provider" error from above, update app.config.ts to import and use provideHttpClient.

[source,typescript] .src/app/app.config.ts

import { provideHttpClient } from '@angular/common/http';

export const appConfig: ApplicationConfig = { providers: [provideRouter(routes), provideHttpClient()] };

Now clicking the search button should work. To make the results look better, remove the <pre> tag and replace it with the following in search.component.html.

[source,xml] .src/app/search/search.component.html

@if (searchResults.length) {

@for (person of searchResults; track person; let i = $index) { }
NamePhoneAddress
{{person.name}}{{person.phone}}{{person.address.street}}
{{person.address.city}}, {{person.address.state}} {{person.address.zip}}
} ----

Then add some additional CSS to search.component.css to improve its table layout.

[source,css] .src/app/search/search.component.css

table { margin-top: 10px; border-collapse: collapse; }

th { text-align: left; border-bottom: 2px solid #ddd; padding: 8px; }

td { border-top: 1px solid #ddd; padding: 8px; }

Now the search results look better.

[[search-results]] .Search results image::src/assets/images/search-results.png[Search Results, 800, scaledwidth="100%"]

But wait, you still don't have search functionality! To add a search feature, add a search() method to SearchService.

[source,typescript] .src/app/shared/search/search.service.ts

import { map, Observable } from 'rxjs'; ...

search(q: string): Observable<Person[]> { if (!q || q === '*') { q = ''; } else { q = q.toLowerCase(); } return this.getAll().pipe( map((data: Person[]) => data .filter((item: Person) => JSON.stringify(item).toLowerCase().includes(q))) ); }

Then refactor SearchComponent to call this method with its query variable.

[source,typescript] .src/app/search/search.component.ts

search(): void { this.searchService.search(this.query).subscribe({ next: (data: Person[]) => { this.searchResults = data; }, error: error => console.log(error) }); }

This won't compile right away.

[source,shell]

[ERROR] TS2345: Argument of type 'string | undefined' is not assignable to parameter of type 'string'. Type 'undefined' is not assignable to type 'string'. [plugin angular-compiler]


Since query will always be assigned (even if it's empty), change its variable declaration to:

[source,typescript]

query!: string; // query: string = ''; will also work

This is called a https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#definite-assignment-assertions[definite assignment assertion]. It's a way to tell TypeScript "I know what I'm doing, the variable will be assigned."

Now search results will be filtered by the query value you type in.

This section showed you how to fetch and display search results. The next section builds on this and shows how to edit and save a record.

== Add an edit feature

Modify search.component.html to wrap the person's name with a link.

[source,html] .src/app/search/search.component.html

{{person.name}} ----

Add RouterLink as an import to search.component.ts so everything will compile:

[source,typescript] .src/app/search/search.component.ts

import { RouterLink } from '@angular/router';

@Component({ selector: 'app-search', standalone: true, imports: [FormsModule, JsonPipe, RouterLink], ... })

Run the following command to generate an EditComponent.

[source]

ng g component edit

Add a route for this component in app.routes.ts:

[source,typescript] .src/app/app.routes.ts

import { EditComponent } from './edit/edit.component';

const routes: Routes = [ { path: 'search', component: SearchComponent }, { path: 'edit/:id', component: EditComponent }, { path: '', redirectTo: '/search', pathMatch: 'full' } ];

Update src/app/edit/edit.component.html to display an editable form. You might notice I've added id attributes to most elements. This is to make it easier to locate elements when writing integration tests.

[source,html] .src/app/edit/edit.component.html

@if (person) {

{{person.name}}

{{person.id}}
项目侧边栏1项目侧边栏2
推荐项目
Project Cover

豆包MarsCode

豆包 MarsCode 是一款革命性的编程助手,通过AI技术提供代码补全、单测生成、代码解释和智能问答等功能,支持100+编程语言,与主流编辑器无缝集成,显著提升开发效率和代码质量。

Project Cover

AI写歌

Suno AI是一个革命性的AI音乐创作平台,能在短短30秒内帮助用户创作出一首完整的歌曲。无论是寻找创作灵感还是需要快速制作音乐,Suno AI都是音乐爱好者和专业人士的理想选择。

Project Cover

有言AI

有言平台提供一站式AIGC视频创作解决方案,通过智能技术简化视频制作流程。无论是企业宣传还是个人分享,有言都能帮助用户快速、轻松地制作出专业级别的视频内容。

Project Cover

Kimi

Kimi AI助手提供多语言对话支持,能够阅读和理解用户上传的文件内容,解析网页信息,并结合搜索结果为用户提供详尽的答案。无论是日常咨询还是专业问题,Kimi都能以友好、专业的方式提供帮助。

Project Cover

阿里绘蛙

绘蛙是阿里巴巴集团推出的革命性AI电商营销平台。利用尖端人工智能技术,为商家提供一键生成商品图和营销文案的服务,显著提升内容创作效率和营销效果。适用于淘宝、天猫等电商平台,让商品第一时间被种草。

Project Cover

吐司

探索Tensor.Art平台的独特AI模型,免费访问各种图像生成与AI训练工具,从Stable Diffusion等基础模型开始,轻松实现创新图像生成。体验前沿的AI技术,推动个人和企业的创新发展。

Project Cover

SubCat字幕猫

SubCat字幕猫APP是一款创新的视频播放器,它将改变您观看视频的方式!SubCat结合了先进的人工智能技术,为您提供即时视频字幕翻译,无论是本地视频还是网络流媒体,让您轻松享受各种语言的内容。

Project Cover

美间AI

美间AI创意设计平台,利用前沿AI技术,为设计师和营销人员提供一站式设计解决方案。从智能海报到3D效果图,再到文案生成,美间让创意设计更简单、更高效。

Project Cover

AIWritePaper论文写作

AIWritePaper论文写作是一站式AI论文写作辅助工具,简化了选题、文献检索至论文撰写的整个过程。通过简单设定,平台可快速生成高质量论文大纲和全文,配合图表、参考文献等一应俱全,同时提供开题报告和答辩PPT等增值服务,保障数据安全,有效提升写作效率和论文质量。

投诉举报邮箱: service@vectorlightyear.com
@2024 懂AI·鲁ICP备2024100362号-6·鲁公网安备37021002001498号