angular框架的成绩管理(增删改查)

1.应用简介

页面链接:https://zmh914.github.io/grade/
​ 应用为基于angular框架的简易“学生成绩管理平台”,对学生成绩数据进行增、删、改、查等操作。内容包含根组件、学生组件、成绩详情组件、不及格组件、消息组件、学生查询组件。

​ 组件间建立路由进行交互,组件只负责信息的展示,信息的获取与更改由服务进行,通过不同组件间注入相同的服务实现组件间的数据共享,且将数据的显示与操作进行分离后,降低组件间的耦合性,使得后期应用修改更新更加便捷。

2.开发过程

2.1服务说明

student.service.ts

​ 本服务包含学生信息的增、删、改、查等方法,通过将本服务注入到其他组件中,调用服务方法,完成信息的获取与展示。并注入了消息服务,在完成一个操作后便发送一条消息。

import { Injectable } from '@angular/core';
import { Student } from './student';
import { Observable, of } from 'rxjs';
import { MessageService } from './message.service';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { catchError, map, tap } from 'rxjs/operators';

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

    //虚拟内存API链接
  private studentsUrl = 'api/students';  // URL to web api    

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

    //获取全部学生信息
  getStudents(): Observable<Student[]> {
    return this.http.get<Student[]>(this.studentsUrl).pipe(tap(_ =>
      this.log('fetched students')), catchError(this.handleError<Student[]>('getStudents', [])));
  }

    //获取单个学生信息
  getStudent(id: number): Observable<Student> {
    const url = `${this.studentsUrl}/${id}`;
    return this.http.get<Student>(url).pipe(
      tap(_ => this.log(`fetched student id=${id}`)),
      catchError(this.handleError<Student>(`getStudent id=${id}`))
    );
  }

 //错误处理
  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(`StudentService: ${message}`);
  }

  //成绩更新服务
updateGrade(student: Student): Observable<any> {
  return this.http.put(this.studentsUrl, student, this.httpOptions).pipe(
    tap(_ => this.log(`updated ${student.name} grade=${student.grade}`)),
    catchError(this.handleError<any>('updateGrade'))
  );
}

//添加学生信息服务
addStudent(student: Student): Observable<Student> {
  return this.http.post<Student>(this.studentsUrl, student, this.httpOptions).pipe(
    tap((newStudent: Student) => this.log(`added student w/ id=${newStudent.id}`)),
    catchError(this.handleError<Student>('addStudent'))
  );
}

//删除学生信息服务
deleteStudent(id: number): Observable<Student> {
  const url = `${this.studentsUrl}/${id}`;

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


/* 学生成绩查询 */
searchStudents(term: number): Observable<Student[]> {
  if (!term) {
    return of([]);
  }
  return this.http.get<Student[]>(`${this.studentsUrl}/?id=${term}`).pipe(
    tap( x => x ?
       this.log(`found students matching "${term}"`) :
       this.log(`no students matching "${term}"`)),
    catchError(this.handleError<Student[]>('searchStudents', []))
  );
}


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

}

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 = [];
  }

  constructor() { }
}

2.2组件说明

​ 组件完成各功能部分的界面展示,一个组件包含HTML文件、CSS文件、以及对应组件的TS类文件。

(1)学生组件

​ 显示所有学生名单,并进行学生信息录入。在显示学生信息时调用学生服务中的获取全部学生信息方法,从虚拟内存中获取全部学生的信息。添加了学生成绩详情的组件路由,点击可跳转到成绩详情页面。

​ 录入学生信息时调用学生服务中的添加学生信息服务,将新添加的信息传入虚拟内存。

HTML

<div class="input">
  <label for="new-hero">学生姓名: </label>
  <input id="new-hero" #studentName />
    <!-- (click) passes input value to add() and then clears the input -->
  <button class="add-button" (click)="add(studentName.value);studentName.value=''">
    录入
  </button>
    
</div>

<table>
    <tr>
      <th>学号</th>
      <th>姓名</th>
      <th>删除</th>
    </tr>
    <tr *ngFor="let student of students">
        <td>
          <a routerLink="/detail/{{student.id}}">{{student.id}}</a>     <!--学生成绩详情路由-->
        </td>
        <td>
          <a routerLink="/detail/{{student.id}}">{{student.name}}</a>
        </td>
        <td>
            <button (click)="delete(student)"> x </button>
        </td>
    </tr>
</table>


TS

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

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

  students: Student[];
 //调用服务获取学生信息
  getStudents(): void {
    this.studentService.getStudents().subscribe(students => this.students = students);
  }

 //添加学生信息
  add(name: string): void {
    name = name.trim();
    if (!name) { return; }
    this.studentService.addStudent({ name } as Student)
      .subscribe(student => {
        this.students.push(student);
      });
   }
 //删除学生信息
  delete(student: Student): void {
    this.students = this.students.filter(h => h !==      student);
  this.studentService.deleteStudent(student.id).subscribe();
  }

  constructor(private studentService: StudentService , private messageService: MessageService ) { }
//进入学生组件时便调用获取学生方法
  ngOnInit(): void {
    this.getStudents();
  }

}

(2)成绩详情组件

​ 本组件显示单个学生的成绩信息,并可对其进行更改。在学生组件页面中点击学生名字或id后,跳转到成绩详情组件,并调用学生服务中的获取单个学生信息方法,展示单个学生的成绩信息。

​ 修改成绩时使用插值绑定,然后调用学生服务中的更新学生信息方法,将修改后的成绩信息更新到虚拟内存中。

HTML

<div *ngIf="student">
    <h2>{{student.name }} </h2>
    <div class = "id">
      <span>学号: </span>{{student.id}}
    </div>
    <div class = "grade">
      <label >成绩: </label>
      <input [(ngModel)]="student.grade" placeholder="成绩">         
    </div>
    <button class = "back" (click)="goBack()">返回</button>
    <button class = "save" (click)="save()">保存</button>
  </div>

TS

import { Component, OnInit, Input } from '@angular/core';
import { Student } from '../student';
import { ActivatedRoute } from '@angular/router';
import { Location } from '@angular/common';
import { StudentService } from '../student.service'; 

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

  @Input() student?: Student;

  constructor(
    private route: ActivatedRoute,
    private studentService: StudentService,
    private location: Location
  ) {}

    //调用获取单个学生信息的服务方法
  getStudent(): void {
    const id = 
    Number(this.route.snapshot.paramMap.get('id'));
    this.studentService.getStudent(id).subscribe(student => this.student = student);
  }

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

//调用更新学生成绩信息的服务方法
  save(): void {
    if (this.student) {
      this.studentService.updateGrade(this.student)
        .subscribe(() => this.goBack());
    }
  }


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

}

(3)不及格组件

​ 本组件显示学生名单中不及格学生,调用学生服务中的获取全部学生信息方法,然后选取需要显示的学生名单进行显示。

HTML

<div class="heroes-menu">
    <a *ngFor="let student of students"
        routerLink="/detail/{{student.id}}">
        {{student.name}}
    </a>
</div>

TS

import { Component, OnInit } from '@angular/core';
import { Student } from '../student';
import { StudentService } from '../student.service';

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

  constructor(private studentService: StudentService) { }

  ngOnInit() {
    this.getStudents();
  }

  getStudents(): void {
    this.studentService.getStudents()
      .subscribe(students => this.students = students.slice(0, 8));
  }
}
(4)根组件

​ 根组件显示导航条,作为路由模块的路由出口,导航条链接了其他各个组件的路由链接,进行各个组件间的转换。

HTML

<div class="title">
<img src="assets/成绩单.png" alt="成绩管理" ><h1>{{title}}</h1>
</div>
<ul>
    <li><a routerLink="/students">学生名单</a></li>
    <li><a routerLink="/dashboard">不及格名单</a></li>
    <li><a routerLink="/search">成绩查询</a></li>
    <li><a routerLink="/history">操作历史</a></li>
    <li><a routerLink="/blog">报告</a></li>
</ul>

<router-outlet></router-outlet>

TS

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = '学生成绩管理';
}

(5)消息组件

​ 显示消息内容。

HTML

<div *ngIf="messageService.messages.length">
    
    <button class="clear"
            (click)="messageService.clear()">清空</button>
    <div *ngFor='let message of messageService.messages'> {{message}} </div>
  
</div>

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

2.3路由模块说明

​ 新建一个独立的顶层模块,加载和配置路由器,它专注于路由功能,然后由根模块 AppModule 导入它。

​ 这个模块类的名字叫做 AppRoutingModule,并且位于 src/app 下的 app-routing.module.ts 文件中。

​ 在路由模块中配置各个组件的路由链接地址,在其他模组中导入路由,然后便可以使用超链接通过组件的路由uRL在不同组件间跳转。

app-routing.module.ts

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { StudentsComponent } from './students/students.component';
import { DashboardComponent } from './dashboard/dashboard.component';
import { StudentDetailComponent } from './student-detail/student-detail.component';
import { StudentSearchComponent } from './student-search/student-search.component';
import { MessagesComponent } from './messages/messages.component';
import { BlogComponent } from './blog/blog.component';

const routes: Routes = [
    
 //path: 用来匹配浏览器地址栏中 URL 的字符串。component: 导航到该路由时,路由器应该创建的组件。
 { path: 'detail/:id', component: StudentDetailComponent }, 
 { path: 'students', component: StudentsComponent },
 { path: 'dashboard', component: DashboardComponent },
 { path: 'search', component: StudentSearchComponent },
 { path: 'history', component: MessagesComponent },
 { path: 'blog', component: BlogComponent }
];

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

2.4虚拟内存说明

HttpClient 是 Angular 通过 HTTP 与远程服务器通讯的机制。本次实操由于没有架设服务器,所以选择了模拟数据服务器。

​ 安装angular-in-memory-web-api模块,应用将会通过HttpClient 来发起请求和接收响应,而不用在乎实际上是这个内存 Web API 在拦截这些请求、操作一个内存数据库,并且给出仿真的响应。

in-memory-data.service.ts

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

@Injectable({
  providedIn: 'root',
})
export class InMemoryDataService implements InMemoryDbService {
  createDb() {
    const students = [
      { id: 1, name: '张安', grade: 120 },
      { id: 2, name: '王五', grade: 120 },
      { id: 3, name: 'Bombasto', grade: 120 },
      { id: 4, name: '李四', grade: 120 },
      { id: 5, name: '曹操', grade: 120 },
      { id: 6, name: 'RubberMan', grade: 120 },
      { id: 7, name: 'Dynama', grade: 120 },
      { id: 8, name: '诸葛亮', grade: 120 },
      { id: 9, name: 'Tornado', grade: 120 },
      { id: 10, name: 'Magma', grade: 120 }
    ];
    return {students};
  }

  genId(students: Student[]): number {
    return students.length > 0 ? Math.max(...students.map(student => student.id)) + 1 : 11;
  }
}

3.问题记录与解决方法

3.1图片无法显示

​ 开始时按照原先静态页面的方法将图片与HTML文件放到同一文件夹中,使用标签进行图片的显示,但是图片无法显示。

​ 经过查阅博客资料,得知需将图片文件放到src/assets中,然后调用图片便可以显示。

3.2GitHub部署

​ 使用angular-cli-ghpages工具进行打包发布,需要先编译项目,然后将编译后的项目文件推送到GitHub仓库,开始时,使用了错误的github仓库链接进行编译,导致页面无内容显示。

​ 修改正确GitHub仓库地址后,便可正常显示内容。

3.3未解决问题

​ 在添加学生信息时,想多个信息同时添加,但是添加后无法调用学生成绩详情,新添加的信息在删除后不会对虚拟数据进行更改,重新加载组建后还是会显示。

​ 所以在添加信息时,今天加了姓名信息,随机生成id信息,然后进入详情页面进行成绩录入。

4.总结

​ 通过这样一个简单的angular项目,对现在的前端应用框架有了基本的认识。与静态网站不同,动态网站更加适合大型应用的开发,各组件与服务的分离,使得结构分工明确,更加便于应用开发与后期更新操作。

​ 有了angular的基础,再去学习其他前端框架会更加容易。本次实操进行了简单的应用,对模块、组件与服务等进行了练习。功能尚不完整,代码书写尚未规范,未采用样式框架,界面不美观,后续还需要改进。

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值