学习Angular官方SuperHero tutorial

附带在做项目的过程之中遇到的问题和解决方法

Posted by Haiming on May 7, 2019

开始学习 Angular 。下面是对于官方教程之中自认为难点或者重点的梳理。 在文章的最后是在项目之中所遇到的问题和解决方法,持续更新~

快速上手

  1. Components are the fundamental building blocks of Angular applications.所以在 Angular 之中,承担功能的基本单位就是 Components。
  2. 使用 Angular CLI 创建好项目之后,会默认创建一个 Angular 组件,是 app-root, 也叫做根组件。

应用的“外壳”

这一章主要是讲如何修改页面的显示内容和样式。

src/app 这个文件夹下面有实现 AppComponent 的三个文件:

  • app.component.ts : 组件的类代码,使用 TypeScript 写成
  • app.component.html : 组件的模板,是使用 HTML 写成
  • app.component.css : 组件的私有 CSS 样式

双花括号语法

`` 在 Angular 之中是插值绑定语法,其意为将组件的 title 属性的值绑定在 HTML 的某些标记之中。如下面的代码,就是把值绑定到 <h1> 标签之中

<h1></h1>

CSS

在这章教程的CSS之中,可看出其提供的CSS一般包含:

  • color
  • font-family : 字体
  • font- size: 字号

一般都是给某些限定的字体,比如

h1 {
  color: #369;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 250%;
}

但是想要在这些标签之外默认字体设置怎么办?用下面的:

/* everywhere else */
* {
  font-family: Arial, Helvetica, sans-serif;
}

* 就代表了默认的选项。

总结

这一章之后,学会了:

  • 使用 Angular CLI 创建初始应用结构
  • 使用 Angular 组件显示数据
  • 使用 来插值。

1. 英雄编辑器

在命令行之中使用 ng generate component heroes 来新建组件, 名为 heroes。

新建组件之后,可以看到其新建四个文件,除了上面提到的三个,还有一个以spec.ts 结尾的测试文件,用于测试。

下面是heroes.component.ts 之中的部分代码:

@Component({
  selector: 'app-heroes',
  templateUrl: './heroes.component.html',
  styleUrls: ['./heroes.component.css']
})

CLI 自动生成了三个元数据属性:

  1. selector - 组件的选择器
  2. templateUrl - 组件模板文件的位置
  3. styleUrls - 组件私有 CSS 样式表文件的位置

其中 app-heroes 用来在父组件的模板之中匹配 HTML 元素的名称。也就是说,是用来在其他文件,例如在 app.component.html 之中插入的调用名称。

ngOnInit() 是一个生命周期钩子,在创建完组件之后就会调用,以完成复杂的初始化过程。

且一定要将这个 class export, 不然其他的地方没法使用。

使用 UppercasePipe 格式化

在 Angular 之中,可以使用 Pipe 表达式来将其格式化。相当于是 pipe 符号左边的数据经过 Pipe 处理之后再流出管道。例如

` <h2> Details</h2>`

就可以看作是 hero.name 经过 uppercase 之后其值变为大写再流出。

双向绑定

<div>
  <label>name:
    <input [(ngModel)]="hero.name" placeholder="name">
  </label>
</div>

此处使用ngModel 可以实现数据的双向绑定,但是不可以直接使用。原因是这个属于 FormsModule 下面的模块需要在 app.module.ts 之中注册。

首先是:

import {FormsModule} from '@angular/forms';

然后在下面的ngModule 里面在进行引入:

@NgModule({
  declarations: [
    AppComponent,
    HeroesComponent
  ],
  imports: [
    BrowserModule,
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})

注意其中已经 imports 了这个 Module

2. 显示英雄列表

*ngFor 列出所有英雄

在<li> 标签之中使用 *ngFor 来列出所有英雄,其作用范围也是 <li> 标签。

<h2>My Heroes</h2>>
<ul class="heroes">
    <li *ngFor="let hero of heroes" (click)="onSelect(hero)" [class.selected]="hero==selectedHero">
        <span class="badge"></span>
    </li>
</ul>

注意此处的 <ul class="heroes"> 是为了下面的CSS 绑定做铺垫,而 [class.selected]="hero==selectedHero" 的作用我们之后再讲。

千万不要忘记 ngFor 的星号

为什么一定要带星号?

看看下面代码有什么特别的?

<div *ngIf="hero"></div>
<div *ngFor="let hero of heroes"></div>

上面指令名称有前缀星号(*),星号是一个语法糖,简化了 ngIfngFor 二者的读写,在模板引擎里最后还是会转换成 <template> 模式。

以下是 ngIf 语法糖与 <template> 的写法:

<!-- Examples (A) and (B) are the same -->
<!-- (A) *ngIf paragraph -->
<p *ngIf="condition">
  Our heroes are true!
</p>

<!-- (B) [ngIf] with template -->
<template [ngIf]="condition">
  <p>
    Our heroes are true!
  </p>
</template>

只要不傻肯定用第一种写法。值得注意的是从A扩展到B,先将段落及其内容移到 <template> 标签里面,属性绑定部分作为 <template> 标签的指令。

接下来看看 *ngFor 是如何转换:

<!-- Examples (A) and (B) are the same -->

<!-- (A) *ngFor div -->
<div *ngFor="let hero of heroes"></div>

<!-- (B) ngFor with template -->
<template ngFor let-hero [ngForOf]="heroes">
  <div></div>
</template>

基本上和 ngIf 如出一辙;额外的细微是多了 ngForOf 属性绑定和 hero 本地模板变量。

主从结构

当用户在主列表点击一个英雄时,该组件在页面底部显示所选英雄的详情。上面代码之中的(click)="onSelect(hero)" 就是一个事件绑定语法的例子。click 外面的圆括号会让 Angular 监听这个 <li> 元素的 click 事件,当用户点击 <li> 时, Angular 就会执行表达式 onSelect(hero)

使用 *ngIf 隐藏空白的详情

一段代码块,如果不想让其产生作用,那么就使用<div>将其包裹起来,同时在标签内部使用 <div *ngIf>来作为条件判断。

使用样式绑定

前面我们所提到的那段代码,[class.selected]="hero==selectedHero" ,其作用就是在我们的 HTML 之中将 CSS 之中已有的样式绑定上去。

其含义为将 CSS 之中的 “heroes.selected”(因为class是heroes) 绑定到hero和selectedHero 相同的Hero上面去,也就是点谁谁变。

小结

个人小结:

在学习到这里的过程中,我个人认为,Angular 的纽带其实是模板,也就是HTML文件。模板通过绑定,可以连接到 Components 之中的方法, 可以使用 *ngIf 或者是*ngFor 等等来进行条件筛选。Component更多的是提供逻辑模块或者是变量运算。样式的话 CSS 也是通过在模板页面进行引用来获得的。

官方小结:

  • 可以选择一个英雄,并查看英雄的详情。
  • 使用*ngFor 显示一个列表
  • 使用 *ngIf 包含或者排除一段HTML
  • 使用 class 绑定来切换 CSS 的样式类

3. 主从组件

这一节主要是讲如何将上一节之中提到过的 html 根据不同的功能分开,也就是将app-heroapp-hero-details放在不同的模块之中。这样做有:

  • 可以单独修改 HeroesComponent 或者 HeroDetailComponent ,而不需要对其他组件进行变动。
  • 可以在其他地方复用 HeroDetailComponent 。

生成Component

ng generate component hero-detail

同样,会生成一个 ts 文件,一个 HTML 文件,一个专属的 CSS 文件,还有一个测试文件。与此同时,其会将 HeroDetailComponent 添加到 src/app/app.module.ts 之中的 @NgModule 的 declarations 之中。

模板分割

将 HeroesComponent 的模板底部的 HTML 代码粘贴到生成的 HeroDetailComponent 之中。

由于在新的 Component 之中暂时没有 selectedHero 属性,所以要将其改成 Hero 。

hero-detail-component.html

<div *ngIf="hero">

  <h2> Details</h2>
  <div><span>id: </span></div>
  <div>
    <label>name:
      <input [(ngModel)]="hero.name" placeholder="name"/>
    </label>
  </div>

</div>

添加 @Input() hero 属性

因为要让我们的 HeroDetailComponent 被外部的 HeroesDetail 所使用,所以我们要在其中添加好 @Input() 的属性。同时因为上面已经将模板之中的所有 selectedHero 改成了 hero , 所以我们还要在 ts 文件之中将 hero添加进去。

hero-detail.component.ts

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

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

  @Input() hero:Hero;

  constructor() { }

  ngOnInit() {
  }

}

注意这里面我们说了要有 Input() 属性,所以在第一行的 @angular/core 之中要多加一个 Input 。下面的 @Input() hero:Hero; 即为将 hero 属性用 @Input 来修饰。

修改 HeroesComponent 的模板

父子关系之中,要将父类的模板修改,使之可以使用子类。

在模板的最下面加入一行:

<app-hero-detail [hero]="selectedHero"></app-hero-detail>

其中的 [hero]="selectedHero" 是一种单向绑定语法。即从 HeroesComponent 的 selectedHero 属性绑定到目标元素的 hero 属性,并且映射到 HeroDetailComponent 的 hero 属性。

小结

  • 创立了一个独立的,可复用的 HeroDetailsComponent 组件
  • 使用属性绑定将父组件 HeroesComponent 控制子组件 HeroDetailComponent
  • 使用 @Input 装饰器使 hero 可以在外部被 HeroesComponent 绑定。

4.服务

个人对于 ts 之中 class 写法的看法

ts 之中我们经常需要去写 export 的 class 去给其他类使用。在 ts 之中我认为分为四部分:

  • 变量初始化: 比如初始化变量 hero, 初始化对象等等
  • constructor: 用来对类进行实例化,只会调用一次
  • ngonInit : 用来对类进行初始化。
  • function : 再就是去写很多的功能,所有功能都在这个部分被定义。

为什么需要服务

一般而言,我们都会将整个Web Application 分成几个部分,部分之间通过引用和注释来相互调用,这样方便代码的修改。之前的教程之中讲到了将 component 变成父子结构,在父子结构之中进行相互调用。这样对于修改或者扩张,都可以保证最小程度的进行改动。

在Web Application 之中,我们肯定是需要进行数据的存取的。数据存取是前端和服务器之间进行的操作,由于网络有延迟,所以一般都要使用异步操作。前端先暂时响应着页面,同时等待服务器传来的数据。一般来说,我们都会使用一个单独的 Service 组件进行操作。

传统分层也是如此,展示页面—Controller 负责逻辑— Service 负责和数据库之间进行连接传输。因此这一节主要内容是Service的创建和调用。

修改HeroesComponent

要在 HeroesComponent 的 constructor 之中加入一个属性 private 的 Service 。这里只是需要 Private 属性就足够,但是下面的 messageService 注入我们必须使用 public 属性,原因之后会提。

constructor(private heroService: HeroService) { }

这段话完成了两个事情:一个是声明了一个私有 heroService 属性,一个是将其标记为一个 HeroService 的注入点。

添加 getHeroes()

稍后这段代码会改为需要Observation的格式。但是现在暂时还是从内存之中得到值。

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

然后在 ngOnInit() 之中调用。

插一句话:上面我们分析了在一个class之中有两个东西,一个是 constructor, 一个是 ngOnInit() 。 Angular 只能控制其中的 ngOnInit() ,也不知道这个是不是在教程之中强烈推荐将 getHeroes() 放到 ngOnInit() 之中使用而不是放在 constructor() 的原因,哈哈哈哈哈。所以最好在 constructor() 之中只做依赖注入操作。

可观察 (Observable) 的数据

前面的

this.heroes=this.heroService.getHeroes();

是同步获取数据的方式。但是在现实生活之中,所有的数据获取都是异步的,异步的话可以返回回调函数(callback),承诺(Promise)和可观察对象(Observable)。

在我们这节之中将会使用 Observable , 因为其最终会使用Angular 的 HttpClient.get 方法来获得数据,而 HttpClient.get() 所返回的对象就是 Observable 。

可观察对象版本的 HeroService

Observable 是 RxJS 库之中的一个关键类。

这节之中使用 RxJS 的 of() 函数来模拟从服务器返回数据。

hero.service.ts

import { Observable, of } from 'rxjs';

......

  getHeroes():Hero[]{
    return HEROES;
  }

这种情况下 of(HEROES) 会返回一个 Observable<Hero[]> ,其会发出单个值,这个值就是模拟这些英雄的数组。

heroes.component.ts 之中将 getHeroes() 换成以下代码:

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

上面的 subscribe() 之中的 heroes => this.heroes = heroes

意味将接收进来的值设为 hero1es , 然后将这个 hero1es 赋值给一开始架设的 heroes 变量。

显示消息

首先创建一个 MessageComponent

ng generate component messages

然后将其先添加到 app.component.html 之中,方式和之前一样。下面是添加之后的代码:

app.component.html

<h1></h1>
<app-heroes></app-heroes>
<app-messages></app-messages>

再创建一个 MessageService

ng generate service message

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

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

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

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

其中定义了一个 messages 作为缓存,两个方法: add() 用于添加一条消息 和 clear() 用于清空 messages 缓存。之外再使用export 将这些全都暴露出去。

将其注入到 HeroService 之中

将 HeroService 之中 import 这个 MessageService ,之后在 constructor 之中加入

constructor(private messageService: MessageService) { }

现在我们的 HeroService 之中注入了 MessageService ,是一个典型的 ” 服务中的服务“ 场景。

从 HeroService 之中显示消息

将 MessagesComponent 之中加入 MessageService , 之后在其 constructor 之中加入:

constructor(public messageService: MessageService) {}

**此处的 messageService 是 public **

原因?是因为要在其模板 HTML 之中直接加入 messageService 的属性,所以这种直接在模板之中使用的情况必须是 public 。

5. 路由

本节的主要目标是将之前由 onSelect() 定义获取的变量,使用链接的形式进行存取。即将每一个 HeroDetailComponent 使用对应的 id 进行读取

添加 AppRoutingModule

在 Angular 之中,最好使用一个独立的顶级模块之中加载和配置路由器,只是专注于路由功能。导入使用 AppModule 。

使用CLI 生成路由

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

  • ` –flat` 将这个文件放到了 src/app 之中,而不是单独目录之中。因为有的情况可能并不止一张路由表,不只在顶级模块之中有定义,所以要在此处指明
  • --module=app 的作用是告知 CLI 将其注册到 AppModule 的 imports 数组之中。

在 AppRoutingModule 之中添加 RouterModule

import { RouterModule, Routes } from '@angular/router';

@NgModule({
  exports: [ RouterModule ]
})

此处将文件成为一个路由表的所需引用已经导入好了,之后将 RouterModule 导出,这样可以使路由器的相关指令在 AppModule 的组件之中使用。

添加路由定义

在路由表之中配置哪个 URL 去往哪个 Component。

典型的一条路由表有两个属性:

  1. path:用于匹配URL
  2. component:当导航到此路由时,应该创建哪个组件
import { HeroesComponent }      from './heroes/heroes.component';

const routes: Routes = [
  { path: 'heroes', component: HeroesComponent }
];

上面这一段意味着将 localhost:4200/heroes 导航到 HeroesComponent 组件之上。

RouterModule.forRoot()

用于初始化路由器,并且开始监听浏览器之中的地址变化。

将 RouterModule添加到 imports 之中,并且参数位置填上 routes。

imports: [ RouterModule.forRoot(routes) ],

这个方法之所以叫 forRoot(), 是要在应用的 root 层面去配置这个router。这个方法会提供路由所需的初始化,并且基于当前浏览器的URL 进行第一次导航。

添加路由出口(RouterOutlet)

将 AppComponent 之中的 换成 元素。

不可能让用户每次都在地址栏之中使用 URL 进行抓取导航。所以要在页面之上设置一个链接,让其可以链接到某个 component 。

官方教程:在 app.component.html 之中添加

<nav>
    <a routerLink="/heroes">heroes</a>
</nav>
  • <nav> :HTML导航栏 (<nav>) 描绘一个含有多个超链接的区域,这个区域包含转到其他页面,或者页面内部其他部分的链接列表.

    使用说明:

    • 并不是所有的链接都必须使用<nav>元素,它只用来将一些热门的链接放入导航栏,例如footer元素就常用来在页面底部包含一个不常用到,没必要加入nav的链接列表.
    • 一个网页也可能含有多个nav元素,例如一个是网站内的导航列表,另一个是本页面内的导航列表.
    • 对于屏幕阅读障碍的人,可以使用这个元素来确定是否忽略初始内容.

添加仪表盘视图

弄一个仪表盘视图,看着好看一点。

ng generate component dashboard

代码在这,自己copy

随堂讨论部分:

  • 此时的 *ngFor 还没有任何的链接绑定,只是鼠标挪上去会变成小手,点击无作用
  • 链接被 dashboard.component.css 之中的样式格式化成了一些色块。

注意此处的 slice 用于将返回的 heroes 筛选第2到第5片之后放到页面上。

添加仪表盘路由与默认路由

在已经添加好仪表盘路由的基础上,默认路由可以这样设计:

{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },

pathMatch 有什么作用呢?其有两种模式,一种是 full , 一种是 prefix 。 full 的话必须要匹配完整的 URL 才可以,就比如 path:test ,其意为只有 localhost:4200/test 这个网址才能跳转到 redirectTo 的页面,但是localhost:4200/test/1就不可以。如果将 pathMatch 这个属性设置为 prefix ,其就意味着例如 ` localhost:4200/test,或者 localhost:4200/test/1这样的页面都可以匹配到 /dashboard`这个路由。

导航到英雄详情

在接下来的设计之中,应该可以做到:

  • 在 dashboard 之中点击某个链接来导航到某个英雄
  • 在英雄列表之中点击某个链接来导航到某个英雄
  • 通过一个”深链接“ URL 粘贴到浏览器的地址栏来指定要显示的英雄

所以要将 HeroDetailComponent 从 HeroesComponent 之中解放出来,换成使用链接方式的访问。

添加英雄详情访问

首先将带有 id 的导航信息放到路由之中

import { HeroDetailComponent }  from './hero-detail/hero-detail.component';

...

{ path: 'detail/:id', component: HeroDetailComponent },

注意这个地方的冒号是占位符,表示某个特定英雄的 id。

在这个之后,所有的路由表已经就绪了,下面可以对照一下和自己的是否相同。

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

注意上面这个最后一条只是我为了测试 prefix 属性使用的,本身并无用处。

DashboardComponent 之中的英雄链接

所以回头看,之前的 <router-outlet> 之中实际是看其 URL 进行导航。由于在之前的路由表之中已经有了 { path: '', redirectTo: '/dashboard', pathMatch: 'full' },所以在一开始的时候其 URL 就会自动跳转到 dashboard, 而 dashboard 又对应着 dashboardComponent ,所以默认就会自动变为 URL:http://localhost:4200/dashboard,在 <router-outlet> 部分也会变成 dashboard 的样式。

下面是将链接插入所有的 dashboard 项之中:

<a *ngFor="let hero of heroes" class="col-1-4"
    routerLink="/detail/">
  <div class="module hero">
    <h4></h4>
  </div>
</a>

同样的,对 heroesComponent 之中也做同样的操作。

<ul class="heroes">
  <li *ngFor="let hero of heroes">
    <a routerLink="/detail/">
      <span class="badge"></span> 
    </a>
  </li>
</ul>

支持路由的 HeroDetailComponent

要将其改成:

  • 获取创建本组件的路由
  • 从这个路由之中获得 id
  • 通过 HeroService 从服务器之中获取到具有这个 id 的英雄数据。

为了实现上面的三个功能,其需要先导入语句:

import { ActivatedRoute } from '@angular/router';
import { Location } from '@angular/common';

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

...

constructor(
  private route: ActivatedRoute,
  private heroService: HeroService,
  private location: Location
) {}
  • ActivatedRoute 用来提取路由之中的参数
  • Location 用来保存点击的路径,可以实现返回上一个链接等操作
  • heroService 是我们自己写的使用 Observation 来返回 hero 数据的方式。

从路由之中提取id


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

  getHero():void{
    const id=+this.route.snapshot.paramMap.get('id');
    console.log("The id wiht + is "+id);
    this.heroService.getHero(id).subscribe(hero=>this.hero=hero);
  }

route.snapshot 是路由信息的静态快照,而 paramMap.get() 就是从URL 之中提取 id的方法。

路由参数总是字符串,而JavaScript 的 (+) 操作符会把字符串转换成数字,英雄的 id 就是数字类型。

添加 HeroService.getHero()

在 HeroService 之中添加下面的 getHero() 方法

getHero(id: number): Observable<Hero> {
  // TODO: send the message _after_ fetching the hero
  this.messageService.add(`HeroService: fetched hero id=${id}`);
  return of(HEROES.find(hero => hero.id === id));
}
  • id 接收的是参数
  • messageService 之中 add 的信息是使用反引号 ` 来括起来的,就是为了嵌入 id

使用回退

<button (click)="goBack()">go back</button>

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

现在可以使用 back 按钮来直接回退了。

官方小结

  • 添加了路由器,用于各个不同组件之间导航
  • 使用 <a> 和一个 <router-outlet> 将 AppComponent 变成了一个只是导航使用的壳
  • 将一个紧耦合的主从视图变成了带路由的详情视图
  • 在多个组件之间共享了 HeroService 服务

6. HTTP

!== is strictly comparing variables on both sides for their VALUE and TYPE. The result will be true if either VALUE or TYPE is inconsistent. While != is more “tolerant”, that it is going to return true only if VALUE are different from the two sides of comparison.

7.在使用Angular之中遇到的疑问和解释

1.为什么所有 method 之中使用 class 的变量需要加上 this?

我认为是这样的:

在很多函数之中,我们都会有传入值,比如

outside:String;

onSubmit(parameter1,parameter2)
{
    //Error:because forget "this"
    outside="Just for test"
}

上面这段代码之中,outside 因为前面没有加上 this 做限定词,所以报错。

个人认为,原因在于不加 this 的话,编译器会从函数的 parameter 之中寻找是否有这个变量,如果没有的话,就会直接报错。

而如果想要使用其所属的 class 之中的变量,则需要在前面加上 this 予以限定。

2. 为什么 Service 需要放在 constructor 之中,而不是放在 ngOnInit() ?

Service 与其他东西不同点在于:

Service 一般都有 @Injectable 这个属性

@Injectable({
    providedIn: 'root'
})

那么在这个 component 之中,下面是 constructor() 部分的代码:

constructor(private heroService: HeroService) { }

这个参数同时做了两件事:1. 声明了一个私有 heroService 属性,2. 把它标记为一个 HeroService 的注入点。

当 Angular 创建 HeroesComponent 时,依赖注入系统就会把这个 heroService 参数设置为 HeroService 的单例对象。

3. =>到底是干什么用的?

我们经常可以看到:

          this.someService.someMethod(this.someValue).subscribe(
            response=>{
              //Doing some functions here
            }
          )

发现对于 Observable ,总是有一个 subscribe 和一个 response 。而且 response 后面总是跟着 =>。网上也没找到很多资料,就我个人在实际操作之中的理解,其意义就是在得到 response 之后执行后面大括号之中的代码,而且大括号之中的代码可以直接使用 response。

4. [ ] 在 Angular之中是什么意思?

在 Angular 之中,会看到在 img 的标签之中的 src 是以 [src] 形式出现的。其他地方很少见这种操作。那么为什么要加入 [ ]这个呢?

原因是 [src] 是相对于 img 标签的输入,对于控件的输入,要使用 [ ]括起来,这里面亲测如果不括起来的话,会直接将后面的字符串作为src来代入,例如 src="image",其不会将 component 之中的 image 变量传入,只会将 ”image“ 这个字符串作为链接,然后访问例如localhost:8888/image 这样的网页,并且给你抛出一个 404

5.<mat-table> 之中的设置

在使用<mat-table>的过程之中,发现始终提示cannot find column called 'id',自认为所有的数据和参数都按照官方示例设置正确了,google 一看,发现是因为其中的属性 matColumnDef之中的设置不对。这个地方应该和在 ts 文件之中设置的相同。这里要更加注意。

6.如何在Angular之中获取网址的参数?

首先在 constructor 之中使用

constructor(private route:ActivatedRoute)

在这个之后可以在 noOnInit() 之中使用下面的方法来获得链接之中的ID:

    this.id = this.route.snapshot.paramMap.get("id");

这里面的 get("id") 不是随便写的,是因为在 app-routing.module.ts 之中有定义:

  { path: 'detail/:id', component: SomeComponent },

三者结合,就可以从网址之中拿到所要的数据了。

7.HQL

HQL和 Angular 实际并不相关,但是都是在本次做项目之中使用到的东西,在这里做一个简单的总结。

HQL(Hibernate Query Languge),是一种面向对象的查询语言,在其中和SQL的 table 和 column 不同,主要概念是 class, object 和 attribute。其语法如下:

[select/update/delete……] from Entity [where……] [group by……] [having……] [order by……]