《通信软件开发与应用》课程结业报告

Angular英雄之旅

本次的web结业作业我是完成了一个基于angular的简单的实现增删改查的一个英雄之旅项目。

开发过程

这次的项目我是用的angular框架,在官方网站找到的教程,我是一步一步跟着教程最后完成了这个项目的开发。
首先先要搭建一个angular项目,在powershell窗口输入如下指令:
在这里插入图片描述
输入yes
在这里插入图片描述
回车选择css。这样我们就创建了一个angular项目。
先创建几个个组件:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

创建服务:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
用如下命令从 npm 中安装这个内存 Web API 包:
在这里插入图片描述
heroes.component.ts代码:

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

import { Hero } from '../hero';
import { HeroService } from '../hero.service';

@Component({
  selector: 'app-heroes',
  templateUrl: './heroes.component.html',
  styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
  heroes: Hero[] = [];

  constructor(private heroService: HeroService) { }

  ngOnInit(): void {
    this.getHeroes();
  }

  getHeroes(): void {
    this.heroService.getHeroes()
    .subscribe(heroes => this.heroes = heroes);
  }

  add(name: string): void {
    name = name.trim();
    if (!name) { return; }
    this.heroService.addHero({ name } as Hero)
      .subscribe(hero => {
        this.heroes.push(hero);
      });
  }

  delete(hero: Hero): void {
    this.heroes = this.heroes.filter(h => h !== hero);
    this.heroService.deleteHero(hero.id).subscribe();
  }

}

heroes.component.html代码:

<h2>My Heroes</h2>

<div>
  <label for="new-hero">Hero name: </label>
  <input id="new-hero" #heroName />

  <!-- (click) passes input value to add() and then clears the input -->
  <button class="add-button" (click)="add(heroName.value); heroName.value=''">
    Add hero
  </button>
</div>

<ul class="heroes">
  <li *ngFor="let hero of heroes" fxLayoutAlign="center center">
    <a routerLink="/detail/{{hero.id}}">

      <div id="lie"><span class="badge">{{hero.id}}</span> {{hero.name}}</div>
    </a>
    <button class="delete" title="delete hero"
      (click)="delete(hero)">x</button>
  </li>
</ul>

heroes.component.css代码:

/* HeroesComponent's private CSS styles */
.heroes {
    width: 100%;
    list-style-type: none;
    text-align: center;
   
  }
  
  input {
   
    width: 40%;
    padding: .5rem;
    margin: 1rem 0;
    box-sizing: border-box;
  }
  
  .heroes li {
    position: relative;
    cursor: pointer;
    text-align: left;
    margin: 1rem 1rem;
    width: 45%;
    float: left;
    
  }
  
  .heroes li:hover {
    left: .1em;
  }
  
  .heroes a {
    color: #333;
    text-decoration: none;
    background-color: #EEE;
    margin: .5em;
    padding: .3em 0;
    height: 1.6em;
    border-radius: 4px;
    display: block;
    width: 100%;
  }
  
  .heroes a:hover {
    color: #2c3a41;
    background-color: #e6e6e6;
  }
  
  .heroes a:active {
    background-color: #525252;
    color: #fafafa;
  }
  
  .heroes .badge {
    display: inline-block;
    font-size: small;
    color: white;
    padding: 0.8em 0.7em 0 0.7em;
    background-color:#405061;
    line-height: 1em;
    position: relative;
    left: -1px;
    top: -4px;
    height: 1.8em;
    min-width: 16px;
    text-align: right;
    margin-right: .8em;
    border-radius: 4px 0 0 4px;
  }
  
  .add-button {
   padding: .5rem 1.5rem;
   font-size: 1rem;
   margin-bottom: 2rem;
  }
  
  .add-button:hover {
    color: white;
    background-color: #42545C;
  }
  
  button.delete {
    position: absolute;
    left: 210px;
    top: 5px;
    background-color: white;
    color:  #525252;
    font-size: 1.1rem;
    padding: 1px 10px 3px 10px;
  }
  
  button.delete:hover {
    background-color: #525252;
    color: white;
  }

hero-detail.component.ts代码:

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Location } from '@angular/common';

import { Hero } from '../hero';
import { HeroService } from '../hero.service';

@Component({
  selector: 'app-hero-detail',
  templateUrl: './hero-detail.component.html',
  styleUrls: [ './hero-detail.component.css' ]
})
export class HeroDetailComponent implements OnInit {
  hero: Hero | undefined;

  constructor(
    private route: ActivatedRoute,
    private heroService: HeroService,
    private location: Location
  ) {}

  ngOnInit(): void {
    this.getHero();
  }

  getHero(): void {
    const id = parseInt(this.route.snapshot.paramMap.get('id')!, 10);
    this.heroService.getHero(id)
      .subscribe(hero => this.hero = hero);
  }

  goBack(): void {
    this.location.back();
  }

  save(): void {
    if (this.hero) {
      this.heroService.updateHero(this.hero)
        .subscribe(() => this.goBack());
    }
  }
}

hero-detail.component.html代码:

<div *ngIf="hero">
    <h2>{{hero.name | uppercase}} Details</h2>
    <div><span>id: </span>{{hero.id}}</div>
    <div>
      <label for="hero-name">Hero name: </label>
      <input id="hero-name" [(ngModel)]="hero.name" placeholder="Hero name"/>
    </div>
    <button (click)="goBack()">go back</button>
    <button (click)="save()">save</button>
  </div>

hero-detail.component.css代码:

label {
    color: #435960;
    font-weight: bold;
  }
  input {
    font-size: 1em;
    padding: .5rem;
  }
  button {
    margin-top: 20px;
    background-color: #eee;
    padding: 1rem;
    border-radius: 4px;
    font-size: 1rem;
  }
  button:hover {
    background-color: #cfd8dc;
  }
  button:disabled {
    background-color: #eee;
    color: #ccc;
    cursor: auto;
  }

messages.component.ts代码:

import { Component, OnInit } from '@angular/core';
import { MessageService } from '../message.service';

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

  constructor(public messageService: MessageService) {}

  ngOnInit(): void {
  }

}

messages.component.html代码:

<p>messages works!</p>
<div *ngIf="messageService.messages.length">

    <h2>Messages</h2>
    <button class="clear"
            (click)="messageService.clear()">Clear messages</button>
    <div *ngFor='let message of messageService.messages'> {{message}} </div>
  
  </div>

dashboard.component.ts代码:

import { Component, OnInit } from '@angular/core';
import { Hero } from '../hero';
import { HeroService } from '../hero.service';

@Component({
  selector: 'app-dashboard',
  templateUrl: './dashboard.component.html',
  styleUrls: [ './dashboard.component.css' ]
})
export class DashboardComponent implements OnInit {
  heroes: Hero[] = [];

  constructor(private heroService: HeroService) { }

  ngOnInit(): void {
    this.getHeroes();
  }

  getHeroes(): void {
    this.heroService.getHeroes()
      .subscribe(heroes => this.heroes = heroes.slice(1, 5));
  }
}

dashboard.component.html代码:

h2 {
    text-align: center;
  }
  
  .heroes-menu {
    padding: 0;
    margin: auto;
    max-width: 1000px;
  
    /* flexbox */
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: space-around;
    align-content: flex-start;
    align-items: flex-start;
  }
  
  a {
    background-color: #3f525c;
    border-radius: 2px;
    padding: 1rem;
    font-size: 1.2rem;
    text-decoration: none;
    display: inline-block;
    color: #fff;
    text-align: center;
    width: 100%;
    min-width: 70px;
    margin: .5rem auto;
    box-sizing: border-box;
  
    /* flexbox */
    order: 0;
    flex: 0 1 auto;
    align-self: auto;
  }
  
  @media (min-width: 600px) {
    a {
      width: 18%;
      box-sizing: content-box;
    }
  }
  
  a:hover {
    background-color: #000;
  }

hero-search.component.ts代码:

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

import { Observable, Subject } from 'rxjs';

import {
   debounceTime, distinctUntilChanged, switchMap
 } from 'rxjs/operators';

import { Hero } from '../hero';
import { HeroService } from '../hero.service';

@Component({
  selector: 'app-hero-search',
  templateUrl: './hero-search.component.html',
  styleUrls: [ './hero-search.component.css' ]
})
export class HeroSearchComponent implements OnInit {
  heroes$!: Observable<Hero[]>;
  private searchTerms = new Subject<string>();

  constructor(private heroService: HeroService) {}

  search(term: string): void {
    this.searchTerms.next(term);
  }

  ngOnInit(): void {
    this.heroes$ = this.searchTerms.pipe(
      debounceTime(300),
      
      distinctUntilChanged(),
      switchMap((term: string) => this.heroService.searchHeroes(term)),
    );
  }
}

hero-search.component.html代码:

<p>hero-search works!</p>
<div id="search-component">
    <label for="search-box">Hero Search</label>
    <input #searchBox id="search-box" (input)="search(searchBox.value)" />
  
    <ul class="search-result">
      <li *ngFor="let hero of heroes$ | async" >
        <a routerLink="/detail/{{hero.id}}">
          {{hero.name}}
        </a>
      </li>
    </ul>
  </div>

hero-search.compponent.css代码:

label {
    display: block;
    font-weight: bold;
    font-size: 1.2rem;
    margin-top: 1rem;
    margin-bottom: .5rem;
    text-align: center;
  
  }
  input {
    padding: .5rem;
    width: 100%;
    max-width: 600px;
    box-sizing: border-box;
    text-align: center;
  }
  
  input:focus {
    outline: #336699 auto 1px;
    text-align: center;
  }
  
  li {
    list-style-type: none;

  }
  .search-result li a {
    border-bottom: 1px solid gray;
    border-left: 1px solid gray;
    border-right: 1px solid gray;
    display: inline-block;
    width: 100%;
    max-width: 600px;
    padding: .5rem;
    box-sizing: border-box;
    text-decoration: none;
    color: rgb(35, 83, 77);
    text-align: center;
  }
  
  .search-result li a:hover {
    background-color: #435A60;
    color: rgb(88, 163, 224);
    text-align: center;
  }
  
  ul.search-result {
    margin-top: 0;
    padding-left: 0;
    text-align: center;
  }

app-routing.module.ts代码:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HeroesComponent } from './heroes/heroes.component';
import { DashboardComponent } from './dashboard/dashboard.component';
import { HeroDetailComponent } from './hero-detail/hero-detail.component';


const routes: Routes = [
  { path: '', redirectTo: '/dashboard', pathMatch: 'full' },
  { path: 'dashboard', component: DashboardComponent },
  { path: 'detail/:id', component: HeroDetailComponent },
  { path: 'heroes', component: HeroesComponent }
];

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

app.component.css代码:

h1 {
    margin-bottom: 0;
  }
  nav a {
    padding: 1rem;
    text-decoration: none;
    margin-top: 10px;
    display: inline-block;
    background-color: #e8e8e8;
    color: #3d3d3d;
    border-radius: 4px;
  }
  
  nav a:hover {
    color: white;
    background-color: #42545C;
  }
  nav a.active {
    background-color: black;
  }
 

app.component.html代码:


<div style="text-align: center;">

  <h1>{{title}}</h1>
  <nav>
    <a routerLink="/dashboard">Dashboard</a>
    <a routerLink="/heroes">Heroes</a>
  </nav>
  <router-outlet></router-outlet>
  <app-messages></app-messages>
</div>

app.component.ts代码:

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

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

app.module.ts代码:

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

import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';
import { InMemoryDataService } from './in-memory-data.service';

import { AppRoutingModule } from './app-routing.module';

import { AppComponent } from './app.component';
import { DashboardComponent } from './dashboard/dashboard.component';
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
import { HeroesComponent } from './heroes/heroes.component';
import { HeroSearchComponent } from './hero-search/hero-search.component';
import { MessagesComponent } from './messages/messages.component';

@NgModule({
  imports: [
    BrowserModule,
    FormsModule,
    AppRoutingModule,
    HttpClientModule,

    HttpClientInMemoryWebApiModule.forRoot(
      InMemoryDataService, { dataEncapsulation: false }
    )
  ],
  declarations: [
    AppComponent,
    DashboardComponent,
    HeroesComponent,
    HeroDetailComponent,
    MessagesComponent,
    HeroSearchComponent
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }

hero.service.ts代码:

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';

import { Observable, of } from 'rxjs';
import { catchError, map, tap } from 'rxjs/operators';

import { Hero } from './hero';
import { MessageService } from './message.service';


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

  private heroesUrl = 'api/heroes';  // URL to web api

  httpOptions = {
    headers: new HttpHeaders({ 'Content-Type': 'application/json' })
  };

  constructor(
    private http: HttpClient,
    private messageService: MessageService) { }

  getHeroes(): Observable<Hero[]> {
    return this.http.get<Hero[]>(this.heroesUrl)
      .pipe(
        tap(_ => this.log('fetched heroes')),
        catchError(this.handleError<Hero[]>('getHeroes', []))
      );
  }

  getHeroNo404<Data>(id: number): Observable<Hero> {
    const url = `${this.heroesUrl}/?id=${id}`;
    return this.http.get<Hero[]>(url)
      .pipe(
        map(heroes => heroes[0]), // returns a {0|1} element array
        tap(h => {
          const outcome = h ? 'fetched' : 'did not find';
          this.log(`${outcome} hero id=${id}`);
        }),
        catchError(this.handleError<Hero>(`getHero id=${id}`))
      );
  }

  getHero(id: number): Observable<Hero> {
    const url = `${this.heroesUrl}/${id}`;
    return this.http.get<Hero>(url).pipe(
      tap(_ => this.log(`fetched hero id=${id}`)),
      catchError(this.handleError<Hero>(`getHero id=${id}`))
    );
  }

  searchHeroes(term: string): Observable<Hero[]> {
    if (!term.trim()) {
      return of([]);
    }
    return this.http.get<Hero[]>(`${this.heroesUrl}/?name=${term}`).pipe(
      tap(x => x.length ?
         this.log(`found heroes matching "${term}"`) :
         this.log(`no heroes matching "${term}"`)),
      catchError(this.handleError<Hero[]>('searchHeroes', []))
    );
  }

  addHero(hero: Hero): Observable<Hero> {
    return this.http.post<Hero>(this.heroesUrl, hero, this.httpOptions).pipe(
      tap((newHero: Hero) => this.log(`added hero w/ id=${newHero.id}`)),
      catchError(this.handleError<Hero>('addHero'))
    );
  }

  deleteHero(id: number): Observable<Hero> {
    const url = `${this.heroesUrl}/${id}`;

    return this.http.delete<Hero>(url, this.httpOptions).pipe(
      tap(_ => this.log(`deleted hero id=${id}`)),
      catchError(this.handleError<Hero>('deleteHero'))
    );
  }

  updateHero(hero: Hero): Observable<any> {
    return this.http.put(this.heroesUrl, hero, this.httpOptions).pipe(
      tap(_ => this.log(`updated hero id=${hero.id}`)),
      catchError(this.handleError<any>('updateHero'))
    );
  }

  private handleError<T>(operation = 'operation', result?: T) {
    return (error: any): Observable<T> => {

      console.error(error); // log to console instead

      this.log(`${operation} failed: ${error.message}`);

      return of(result as T);
    };
  }

  private log(message: string) {
    this.messageService.add(`HeroService: ${message}`);
  }
}

hero.ts代码:

export interface Hero {
    id: number;
    name: string;
  }

in-memory-data.service.ts代码:

import { Injectable } from '@angular/core';
import { InMemoryDbService } from 'angular-in-memory-web-api';
import { Hero } from './hero';

@Injectable({
  providedIn: 'root',
})
export class InMemoryDataService implements InMemoryDbService {
  createDb() {
    const heroes = [
      { id: 11, name: 'Dr Nice' },
      { id: 12, name: 'Narco' },
      { id: 13, name: 'Bombasto' },
      { id: 14, name: 'Celeritas' },
      { id: 15, name: 'Magneta' },
      { id: 16, name: 'RubberMan' },
      { id: 17, name: 'Dynama' },
      { id: 18, name: 'Dr IQ' },
      { id: 19, name: 'Magma' },
      { id: 20, name: 'Tornado' }
    ];
    return {heroes};
  }
  genId(heroes: Hero[]): number {
    return heroes.length > 0 ? Math.max(...heroes.map(hero => hero.id)) + 1 : 11;
  }
}

message.service.ts代码:

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

@Injectable({
  providedIn: 'root',
})
export class MessageService {
  messages: string[] = [];

  add(message: string) {
    this.messages.push(message);
  }

  clear() {
    this.messages = [];
  }
}

遇到的问题

在期间遇到一些小问题:比如说angular的语法,ngif等的错误使用等等。

解决

由于我是跟着官方文档完成的这个项目,所以查看文档就可以解决。

还没有解决的问题

由于我的数据是模拟的服务器的数据,所以是一些死数据,所以服务器这一块还没有搭建。

总结

经过学习,我发现Angular还有一些很好用的特性,比如”订阅“,自带测试框架等。同时,Angular将TypeScript 类文件和HTML模版文件划分开来,更有助于初学者认识、理解html和TypeScript类文件。此外Angular对typeScript进行了严格校验,有助于前端开发者树立类型思维,其编程思维相比于React也 更偏向于“后端”, 感觉掌握了angular前端小伙伴,后续想全栈的话,相对上手也能更快一些。
而且学习angular不仅让我的技术领域跟一部拓展,更让我的学习能力更上一层楼,让我多方面受益匪浅。
项目地址:https://thepp997.github.io/HeroPro/docs/
angular官方教程:https://angular.cn/tutorial/toh-pt1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

余臻

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值