仿制Reddit網站
讀完本章之后, 你將掌握如何構建基本的Angular應用。
- 簡單的應用將涵蓋Angular中的大部分基本要素
- 構建自定義組件;
- 從表單中接收用戶輸入
- 把對象列表渲染到視圖中
- 攔截用戶的點擊操作, 并據此作出反應
起步
TypeScript[1]
- 使用TypeScript, 首先需要安裝Node.js
我必須用TypeScript嗎? 并非如此! 要使用 Angular,TypeScript 并不是必需的, 但它可能是最好的選擇。 Angular也有一套 ES5 API, 但Angular本身就是用 TypeScript 寫成的, 所以人們一般也會選用它。 本書也將使用TypeScript, 因為它確實很棒, 能讓 Angular 寫起來更簡單。 當然, 并不是非它不可
- 使用NPM[2]安裝TypeScript
$ npm install -g typescript
angular-cli
Angular提供了一個命令行工具angular-cli, 它能讓用戶通過命令行創建和管理項目。在本章中, 我們就用它來創建第一個應用
- 安裝angular-cli
//安裝完畢之后, 你就可以在命令行中用ng命令運行它了
$ npm install -g angular-cli@1.0.0-beta.18
//不帶參數運行ng命令時, 它就會執行默認的help命令。 help命令會解釋如何使用本工具
$ ng
Could not start watchman; falling back to NodeWatcher for file system events.
Visit http://ember-cli.com/user-guide/#watchman for more info.
Usage: ng <command (Default: help)>
-
OS X 用戶
* 安裝Homebrew工具 通過 Homebrew 工具來安裝 watchman
* 安裝 watchman 的工具,幫助 angular-cli 監聽文件系統的變化```javascript // 安裝Homebrew工具后 可使用此命令 $ brew install watchman ```
Linux 用戶:學習如何安裝watchman
Windows 用戶:不必安裝任何東西, angular-cli將使用原生的 Node.js 文件監視器
示例項目
- 運行ng new命令(最新版好像不能使用‘_’和‘angular’關鍵字,會提示警告)
$ ng new angular2_hello_world //練習時命名 helloWorld 可以通過
//運行之后輸出:
installing ng 2
create .editorconfig
create README.md
create srcappapp.component.css
create srcappapp.component.html
create srcappapp.component.spec.ts
create srcappapp.component.ts
create srcappapp.module.ts
create srcappindex.ts
create srcappshared/index.ts
create src/assets/.gitkeep
create src/assets/.npmignore
create src/environments/environment.dev.ts
create src/environments/environment.prod.ts
create src/environments/environment.ts
create src/favicon.ico
create src/index.html
create src/main.ts
create src/polyfills.ts
create src/styles.css
create src/test.ts
create src/tsconfig.jsoncreate src/typings.d.ts
create angular-cli.json
create e2e/app.e2e-spec.ts
create e2e/app.po.ts
create e2e/tsconfig.json
create .gitignore
create karma.conf.js
create package.json
create protractor.conf.js
create tslint.json
Successfully initialized git.
( Installing packages for tooling via npm
```
* **npm依賴的安裝** (會自動安裝)
```bash
//提示這行代碼表示安裝依賴完成
Installed packages for tooling via npm.
```
* **進入angular2_hello_world目錄**
```java
$ cd angular2_hello_world
$ tree -F -L 1
.
├──README.md // an useful README
├──angular-cli.json // angular-cli configuration file
├──e2e/ // end to end tests
├──karma.conf.js // unit test configuration
├──node_modules/ // installed dependencies
├──package.json // npm configuration
├──protractor.conf.js // e2e test configuration
├──src/ // application source
└──tslint.json // linter config file
3 directories, 6 files
```
* **進入 src 目錄 查看應用代碼**
```bash
$ cd src
$ tree -F
.|-- app/
| |-- app.component.css
| |-- app.component.html
| |-- app.component.spec.ts
| |-- app.component.ts
| |-- app.module.ts
| |-- index.ts
| `-- shared/
| `-- index.ts
|-- assets/
|-- environments/
| |-- environment.dev.ts
| |-- environment.prod.ts
| `-- environment.ts
|-- favicon.ico
|-- index.html
|-- main.ts
|-- polyfills.ts
|-- styles.css
|-- test.ts
|-- tsconfig.json
`-- typings.d.ts
4 directories, 18 files
```
* **編輯器打開index.html**
```html
//聲明了頁面的字符集(charset) 、 標題(title) 和基礎URL(base href)
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Angular2HelloWorld</title>
<base href="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="imagex-icon" href="favicon.ico">
</head>
<body>
// 應用將會在app-root標簽處進行渲染
文本Loading...是一個占位符,應用加載之前會顯示它,也可以是加載動畫
<app-root>Loading...</app-root>
</body>
</html>
運行應用
//angular-cli有一個內建的HTTP服務器,根目錄運行命令
$ ng serve
** NG Live Development Server is running on http://localhost:4200. **
// a bunch of debug messages
Build successful - 1342ms.
我們的應用正在localhost的4200端口上運行。 打開瀏覽器并訪問 http://localhost:4200
制作Component(組件)
Angular背后的指導思想之一就是組件化。
<select>
、<form>
和<video>
都是由瀏覽器的開發者預先定義好的,我們要教瀏覽器認識一些擁有自定義功能的新標簽
-
使用angular-cli 的 generate 指令創建新組建
$ ng generate component hello-world installing component create srcapphello-world/hello-world.component.css create srcapphello-world/hello-world.component.html create srcapphello-world/hello-world.component.spec.ts create srcapphello-world/hello-world.component.ts
定義新組建
Component注解
組件定義類
打開第一個TypeScript文件:srcapphello-world/hello-world.component.ts
,接下來就會一步步講解它。
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-hello-world',
templateUrl: './hello-world.component.html',
styleUrls: ['./hello-world.component.css']
})
export class HelloWorldComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
TypeScript文件的后綴是.ts而不是.js。 問題在于瀏覽器
并不知道該如何解釋TypeScript文件。 為了解決這個問題, ng
serve命令會自動把.ts文件編譯為.js文件
導入依賴
import
語句定義了依賴的模塊@angular/core
部分告訴程序到哪里查找所需的這些依賴-
import
語句的結構://從另一個模塊中拉取這些依賴,并且讓這些依賴在當前文件中可用 import { things } from wherever
Component注解
導入依賴后, 聲明該組件
//注解其實是讓編譯器為代碼添加功能的途徑之一
//可以把注解看作添加到代碼上的元數據。
//當在HelloWorld類上使用@Component時,
//就把HelloWorld“裝飾”(decorate) 成了一個Component。
@Component({
//selector屬性用來指出該組件將使用哪個DOM元素
selector: 'app-hello-world',
})
用templateUrl添加模板
@Component({
//Angular加載該組件時,就會讀取此文件的內容作為組件的模板
templateUrl: './hello-world.component.html',
})
添加template
@Component({
selector: 'app-hello-world',
//傳入template選項來為@Component添加一個模板
template: `
<p>
hello-world works inline!
</p>
`
})
- ` ... `反引號定義多行字符串,ES6中的一個新特性
templateUrl 和 template 選擇哪種寫法?
視情況而定,把代碼和模板分開。對一些開發團隊來說更容易,不過某些項目會增加成本,因為不得不在一大堆文件之間切換
如果模板行數短于一頁,更傾向于把模板和代碼放在一起(也就是.ts文件中)。同時看到邏輯和視圖部分,便于理解它們的互動。
內聯寫法缺點:編輯器不支持對內部HTML字符串進行語法高亮
用styleUrls添加CSS樣式
Angular使用一項叫作樣式封裝(styleencapsulation) 的技術,它意味著在特定組件中指定的樣式只會應用于該組件本身。
@Component({
//引入 CSS 作為該組件的樣式
同一個組件可以加載多個樣式表
styleUrls: ['./hello-world.component.css']
})
加載組件
把該組件的標簽添加到一個將要渲染的模板中
//<app-hello-world>標簽添加到app.component.html中
<h1>
{{title}}
<app-hello-world></app-hello-world>
</h1>
把數據添加到組件中
- 創建新組建
//顯示用戶的名字
ng generate component user-item
```html
//app-user-item標簽添加到app.component.html中
<h1>
{{title}}
<app-hello-world></app-hello-world>
<app-user-item></app-user-item>
</h1>
```
UserItemComponent顯示一個指定用戶的名字
-
name屬性
- 我們往 UserItemComponent 類添加了一個 name 屬性
- name指定類型是TypeScript中的特性,用來確保它的值必須是string
-
構造函數
- 這個函數會在創建這個類的實例時自動調用
- 可以用模板語法[3]({{ }})在模板中顯示該變量的值
export class UserItemComponent implements OnInit { //name是我們想設置的屬性名,而string是該屬性的類型 name: string; // <-- added name property constructor() { // 組件被創建時, 把name設置為'Felipe' this.name = 'Felipe'; // set the name } ngOnInit() { } } //useritem.component.html 中 <p> Hello {{ name }} </p>
使用數組
- 創建一個會渲染用戶列表的新組件
ng generate component user-list
- 修改 app.component.html
<h1>
{{title}}
<app-hello-world></app-hello-world>
<app-user-list></app-user-list>
</h1>
- 修改 user-list.component.ts
export class UserListComponent implements OnInit {
//語法表示names的類型是string構成的數組
//另一種寫法是Array<string>。
names: string[];
constructor() {
this.names = ['Ari', 'Carlos', 'Felipe', 'Nate'];
}
ngOnInit() {
}
}
- 修改 user-list.component.html
<ul>
//循環處理 names 中的每一個元素并將其逐個賦值給一個名叫 name 的局部變量
//( name 是一個局部變量 可以更換 如foobar )
<li *ngFor="let name of names">Hello {{ name }}</li>
</ul>
NgFor指令將為數組 names 中的每一個條目都渲染出一個 li 標簽,并聲明一個本地變量 name 來持有當前迭代的條目。然后這個新變量將被插值到 Hello {{ name }}代碼片段里
如果你想進一步探索,可以直接閱讀Angular源代碼來學習Angular核心團隊是如何編寫組件的
使用UserItemComponent組件
用UserItemComponent作為子組件,為列表中的每個條目指定模板
- 配置 UserListComponent 來渲染 UserItemComponent
- 配置 UserItemComponent 來接收 name 變量作為輸入
- 配置 UserListComponent 的模板來把用戶名傳給
UserItemComponent
渲染UserItemComponent
//把li標簽替換為app-user-item
標簽
<ul>
<app-user-item *ngFor="let name of names">
</app-user-item>
</ul>
接收輸入
//修改 UserItemComponent
// 引入 Input
import {
Component,
OnInit,
Input // <--- added this } from '@angular/core';
@Component({
selector: 'app-user-item',
templateUrl: './user-item.component.html',
styleUrls: ['./user-item.component.css']
})
export class UserItemComponent implements OnInit {
//添加 @Input 注解
@Input() name: string; // <-- added Input annotation
constructor() {
// 不希望有默認值
// removed setting name
} ngOnInit() {
}
}
傳入Input值
**為了把一個值傳入組件,就要在模板中使用方括號[]語法。 **
// 修改 userlist.component.html
<ul>
<app-user-item *ngFor="let name of names" [name]="name">
</app-user-item>
</ul>
添加一個帶方括號的屬性(比如[foo])意味著把一個值傳給該組件上同名的輸入屬性(比如 foo)
// name 右側的值來自 ngFor 中的 let name ...語句
<app-user-item *ngFor="let individualUserName of names"
[name]="individualUserName">
</app-user-item>
[name]
部分指定的是 UserItemComponent 上的 Input。注意,我們正在傳入的并不是字符串字面量"individualUserName", 而是individualUserName 變量的值, 也就是 names 中的每個元素。
執行過程
- 在 names 中迭代
- 為 names 中的每個元素創建一個新的 UserItemComponent
- 把當前名字的值傳給 UserItemComponent 上名叫 name 的 Input屬性
啟動速成班
Angular應用是如何啟動的?
每個應用都有一個主入口點。該應用是由 angular-cli 構建的,而
angular-cli 則是基于一個名叫 webpack 的工具。 你不必理解webpack 就能使用 Angular, 但理解應用的啟動流程是很有幫助的。
// 通過運行下列命令來啟動
// ng會查閱angular-cli.json文件來找出該應用的入口點
ng serve
大體流程
- angular-cli.json指定一個"main"文件, 這里是main.ts;
- main.ts 是應用的入口點, 并且會引導(bootstrap) 我們的應用;
- 引導過程會引導一個Angular模塊——我們尚未討論過模塊, 不過很快就會談到;
- 我們使用 AppModule 來引導該應用, 它是在srcappapp.module.ts中指定的;
- AppModule 指定了將哪個組件用作頂層組件, 這里是 AppComponent;
- AppComponent 的模板中有一個<app-user-list>標簽, 它會渲染出我們的用戶列表
Angular有一個強大的概念: 模塊。當引導一個 Angular 應用時,并不是直接引導一個組件, 而是創建了一個 NgModule,它指向了你要加載的組件。
// 為 AppModule 類添加了元數據
@NgModule({
declarations: [
AppComponent,
HelloWorldComponent,
UserItemComponent,
UserListComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
@NgModule注解有三個屬性
- declarations(聲明):指定了在該模塊中定義的組件。要想在模板中使用一個組件,你必須首先在NgModule中聲明它
- imports :描述了該模塊有哪些依賴。我們正在創建一個瀏覽器應用,因此要導入BrowserModule
- bootstrap 告訴Angular, 當使用該模塊引導應用時, 我們要把AppComponent加載為頂層組件
擴展你的應用
構造兩個組件
- 整體應用程序,包含一個用來提交新文章的表單
- 每個文章
// 創建一個新的應用
ng new xxx( 應用名字 )
添加CSS
在本項目中,我們將使用 Semantic-UI 來幫助添加樣式。Semantic-UI 是一個CSS框架, 類似于Zurb Foundation 或 TwitterBootstrap 。
完成版示例代碼中復制以下文件到你的應用目錄下:
- src/index.html
- src/styles.css
- srcappvendor
- src/assets/images
應用程序組件
構建一個新的組件
- 存儲我們的當前文章列表
- 包含一個表單, 用來提交新的文章。
// 修改 app.component.html
<form class="ui large form segment">
<h3 class="ui header">Add a Link</h3>
<div class="field">
<label for="title">Title:</label>
<input name="title">
</div>
<div class="field">
<label for="link">Link:</label>
<input name="link">
</div>
</form>
添加互動
-
添加一個提交按鈕
//把事件的名字包裹在圓括號()中就可以告訴Angular: 我們要響應這個事件 <button (click)="addArticle()" class="ui positive right floated button"> Submit link </button>
-
定義一個函數
// 修改 app.component.ts export class AppComponent { addArticle(title: HTMLInputElement, link: HTMLInputElement): boolean { console.log(`Adding article title: ${title.value} and link:${link.value}`); return false; } }
-
修改模板
<form class="ui large form segment"> <h3 class="ui header">Add a Link</h3> <div class="field"> <label for="title">Title:</label> // input標簽上使用了#(hash)來要求Angular把該元素賦值給一個局部變量 <input name="title" #newtitle> <!-- changed --> </div> <div class="field"> <label for="link">Link:</label> <input name="link" #newlink> <!-- changed --> </div> // 通過把#title和#link添加到適當的<input/>元素上,就可以把它們作為變量傳給按鈕上的addArticle()函數 <!-- added this button --> <button (click)="addArticle(newtitle, newlink)" class="ui positive right floated button"> Submit link </button> </form>
四項修改
- 在模版中創建了一個 button 標簽, 向用戶表明應該點擊哪里
- 新建了一個名叫 addArticle 的函數, 來定義按鈕被點擊時要做的事情
- 在 button 上添加了一個(click)屬性, 意思是“只要點擊了這個按鈕, 就運行 addArticle 函數”
- 在兩個
<input>
標簽上分別添加了#newtitle 和 #newlink 屬性
-
綁定input的值
// 注意, 第一個輸入標簽是這樣的: <input name="title" #newtitle>
Angular把這個
<input>
綁定到變量 newtitle 上。 #newtitle 語法被稱作一個解析(resolve),其效果是讓變量 newtitle 可用于該視圖的所有表達式中 。newtitle 現在是一個對象, 它代表了這個inputDOM元素(更確切地說,它的類型是 HTMLInputElement )。由于newtitle是一個對象,我們可以通過newtitle.value表達式來獲取這個輸入框的值 -
把事件綁定到動作
- addArticle是組件定義類AppComponent里的一個函數。
(2) newtitle來自名叫title的<input>
標簽上的解析
(#newtitle) 。
(3) newlink來自名叫link的<input>
標簽上的解析
(#newlink)
- addArticle是組件定義類AppComponent里的一個函數。
<button (click)="addArticle(newtitle, newlink)"
class="ui positive right floated button">
Submit link
</button>
-
定義操作邏輯
- title和link 都是 HTMLInputElement 類型的對象
- 從 input 中獲取值, 就得調用title.value
//${title.value}放在了字符串中, 它最終會被
替換成title.value的值
addArticle(title: HTMLInputElement, link: HTMLInputElement):
boolean {
console.log(`Adding article title: ${title.value} and link: ${link.value}`);
return false;
}
添加文章組件
生成一個新組件
- 在模板中定義了 ArticleComponent 的視圖
- 通過為類加上 @Component 注解定義了 ArticleComponent 組件的元數據
- 定義了一個組件定義類(ArticleComponent) , 其中是組件本身的邏輯
ng generate component article
- 創建 ArticleComponent 的 template
// 修改 article.component.html
// 左側是投票的數量
// four wide column 和 twelve wide column 這兩個 CSS 類
//來指定這兩列。它們來自 Semantic UI 的 CSS 庫
<div class="four wide column center aligned votes">
<div class="ui statistic">
<div class="value">
{{ votes }}
</div>
<div class="label">
Points
</div>
</div>
</div>
// 右側是文章的信息
<div class="twelve wide column">
<a class="ui large header" href="{{ link }}">
{{ title }}
</a>
<ul class="ui big horizontal list voters">
<li class="item">
<a href (click)="voteUp()">
<i class="arrow up icon"></i>
upvote
</a>
</li>
<li class="item">
<a href (click)="voteDown()">
<i class="arrow down icon"></i>
downvote
</a>
</li>
</ul>
</div>
a 標簽的 href 屬性中:href="{{ link }}"。在這種情況下,href 的值會根據組件類的 link 屬性的值進行動態插值計算得出
- 創建ArticleComponent
// 修改 article.component.ts
@Component({
selector: 'apparticle',
templateUrl: './article.component.html',
styleUrls: ['./article.component.css'],
host: {
//apparticle都獨占一行 Semantic UI 用來表示行的CSS類
class: 'row'
}
})
- 創建組件定義類ArticleComponent
// 創建了以下三個屬性
// 1. votes: 一個數字,用來表示所有“贊”減去所有“踩”的數量之和。
// 2. title: 一個字符串, 用來存放文章的標題。
// 3. link: 一個字符串, 用來存放文章的URL
export class ArticleComponent implements OnInit {
votes: number;
title: string;
link: string;
constructor() {
this.title = 'Angular 2';
this.link = 'http://angular.io';
this.votes = 10;
}
voteUp() {
this.votes += 1;
}
voteDown() {
this.votes -= 1;
}
ngOnInit() {
}
}
- 使用apparticle組件
// AppComponent的模板中
<button (click)="addArticle(newtitle, newlink)" class="ui positive right floated button">
Submit link
</button>
</form>
<div class="ui grid posts">
<apparticle>
</apparticle>
</div>
在 AngularJS 中,指令的匹配是全局的;而Angular中,你需要明確指定要使用哪個組件,意味著我們不必被迫在全局命名空間中共享這些指令選擇器。
// app.module.ts
import { AppComponent } from './app.component';
import { ArticleComponent } from './article/article.component.ts';
@NgModule({
declarations: [
AppComponent,
ArticleComponent // <-- added this
],
默認情況下, JavaScript會把click事件冒泡到所有父級組件中。因為click事件被冒泡到了父級,瀏覽器就會嘗試導航到這個空白鏈接,于是瀏覽器就重新刷新了。
解決:我們得讓click的事件處理器返回false。這能確保瀏覽器不會嘗試刷新頁面。
voteDown(): boolean {
this.votes -= 1;
return false;
}
// and similarly with `voteUp()`
渲染多行
創建Article類
// 此目錄下創建文件 article/article.model.ts
// 在MVC模式中, 它被稱為模型(model)
export class Article {
title: string;
link: string;
votes: number;
constructor(title: string, link: string, votes?: number) {
this.title = title;
this.link = link;
this.votes = votes || 0;
}
}
// article.component.ts
import { Article } from './article.model';
export class ArticleComponent implements OnInit {
article: Article;
constructor() {
this.article = new Article(
'Angular 2',
'http://angular.io',
10);
}
voteUp(): boolean {
this.article.votes += 1;
return false;
}
voteDown(): boolean {
this.article.votes -= 1
return false;
}
ngOnInit() {
}
}
// 視圖模型 article.component.html
<div class="four wide column center aligned votes">
<div class="ui statistic">
<div class="value">
{{ article.votes }}
</div>
<div class="label">
Points
</div>
</div>
</div>
<div class="twelve wide column">
<a class="ui large header" href="{{ article.link }}">
{{ article.title }}
</a>
<ul class="ui big horizontal list voters">
<li class="item">
<a href (click)="voteUp()">
<i class="arrow up icon"></i>
upvote
</a>
</li>
<li class="item">
<a href (click)="voteDown()">
<i class="arrow down icon"></i>
downvote
</a>
</li>
</ul>
</div>
當前的voteUp和voteDown違反了迪米特法則。迪米特法則是指:一個對象對其他對象的結構或屬性所作的假設應該越少越好。問題在于ArticleComponent組件了解太多Article類的內部知識了
// article.model.ts
export class Article {
title: string;
link: string;
votes: number;
constructor(title: string, link: string, votes?: number) {
this.title = title;
this.link = link;
this.votes = votes || 0;
}
voteUp(): void {
this.votes += 1;
}
voteDown(): void {
this.votes -= 1;
}
domain(): string {
try {
const link: string = this.link.split('//')[1];
return link.split('/')[0];
} catch (err) {
return null;
}
}
}
// article.component.ts
export class ArticleComponent implements OnInit {
article: Article;
constructor() {
this.article = new Article(
'Angular',
'http://angular.io',
10);
}
voteUp(): boolean {
this.article.voteUp();
return false;
}
voteDown(): boolean {
this.article.voteDown();
return false;
}
ngOnInit() {
}
}
為什么模型和組件中都有一個voteUp函數?
這兩個函數所做的事情略有不同。ArticleComponent 上的 voteUp() 函數是與組件的視圖有關的,而 Article 模型上的 voteUp() 定義了模型上的變化。
我們把大量邏輯移出組件,放進了模型中。與此對應的MVC指南應該是胖模型,皮包骨的控制器;其核心思想是,我們要把大部分領域邏輯移到模型中,以便讓組件只做盡可能少的工作。
存儲多篇文章
// 讓 AppComponent 擁有一份文章集合
// 引入模型
import { Article } from './article/article.model';
export class AppComponent {
//articles 是 Article 的數組。另一種寫法是 Array<Article>
// Array 是一個集合,它只能存放 Article 類型的對象
articles: Article[];
constructor() {
this.articles = [
new Article('Angular 2', 'http://angular.io', 3),
new Article('Fullstack', 'http://fullstack.io', 2),
new Article('Angular Homepage', 'http://angular.io', 1),
];
}
addArticle(title: HTMLInputElement,link: HTMLInputElement): boolean{
console.log(`Adding article title: ${title.value} and link: ${link.value}`);
this.articles.push(new Article(title.value, link.value, 0));
title.value = '';
link.value = '';
return false;
}
}
使用inputs配置ArticleComponent
有了一個Article模型的列表, 該怎么把它們傳給ArticleComponent組件呢?
這里我們又用到了 Input。以前 ArticleComponent 類的定義是下面這樣的
// article.component.ts
export class ArticleComponent implements OnInit {
article: Article;
constructor() {
//構造函數中硬編碼了一個特定的Article; 而制作組件時, 不但要能封裝, 還要能復用
this.article = new Article(
'Angular 2',
'http://angular.io',
10);
}
// 修改 article.component.ts
export class ArticleComponent implements OnInit {
@Input() article: Article;
voteUp(): boolean {this.article.voteUp();
return false;
}
voteDown(): boolean {
this.article.voteDown();
return false;
}
ngOnInit() {
}
}
渲染文章列表
// 修改 AppComponent 模板
// 1. articles是一個Article的數組, 由AppComponent組件定義
// 2. foobar是一個articles數組中的單個元素(一個Article對象)由NgFor定義
// 3. article是一個字段名, 由ArticleComponent中的inputs屬性定義。
Submit link
</button>
</form>
<!-- start adding here -->
<div class="ui grid posts">
<apparticle *ngFor="let article of articles" [article]="article">
</apparticle>
</div>
<!-- end adding here -->
添加新文章
// 修改按鈕 思路:
// 1. 創建一個具有所提交標題和URL的Article新實例
// 2. 把它加入Article數組;
// 3. 清除input字段的值
addArticle(title: HTMLInputElement, link: HTMLInputElement): boolean {
console.log(`Adding article title: ${title.value} and link: ${link.value}`);
this.articles.push(new Article(title.value, link.value, 0));
//修改value屬性時, 頁面中的input標簽也會跟著
改變
title.value = '';
link.value = '';
return false;
}
最后的修整
顯示文章所屬的域名
// article.model.ts
domain(): string {
try {
// 注意:URL必須包含http://
const link: string = this.link.split('//')[1];
return link.split('/')[0];
} catch (err) {
return null;
}
}
// ArticleComponent的模板
<div class="twelve wide column">
<a class="ui large header" href="{{ article.link }}">
{{ article.title }}
</a>
<!-- right here -->
<div class="meta">({{ article.domain() }})</div>
<ul class="ui big horizontal list voters">
<li class="item">
<a href (click)="voteUp()">
基于分數重新排序
//AppComponent上創建一個新方法 sortedArticles
sortedArticles(): Article[] {
return this.articles.sort((a: Article, b: Article) => b.votes - a.votes);
}
// app.component.html
<div class="ui grid posts">
<apparticle *ngFor="let article of sortedArticles()"
[article]="article">
</apparticle>
</div>
全部代碼
總結
Angular程序的寫法
- 把應用拆分成組件
- 創建視圖
- 定義模型
- 顯示模型
- 添加交互。
(第一章完結)
自己練習的代碼,希望對你有用