-
Notifications
You must be signed in to change notification settings - Fork 101
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
编写你的第一个Angular2 Web应用 #24
Comments
简单的Reddit克隆在本章中,我们要构建一个应用程序,允许用户发布了一篇文章(带有标题和URL)并且可以给文章投票 你可以认为这个应用是一个站点的初期,像Reddit或Product Hunt, 在这个简单的应用中,我们将一起涉及到Angular 2大部分内容。 包括:
当你完成这一章你会掌握如何构建基本的Angular 2应用程序。 我们的应用将会和下面的截图看起来差不多 首先,用户提交新的链接后,用户将能够对每篇内容进行upvote和downvote。每一个链接都会有一个分数,可以投票给我们发现有用的链接。 在这个项目和整本书中,我们使用TypeScript来编写,TypeScript是JavaScript的ES6的超集,增加了数据类型。在本章中我们不会深度讨论TypeScript,但如果你熟悉ES5/ES6,那应该没有任何问题。 我们会在下一章深度了解TypeScript。如果你遇到了一些新的语法无需太担心。 |
快速入门TypeScript开始使用TypeScript前,你需要先安装Node.js。有很多不同的方法安装Node.js,请参阅Node.js的网站https://nodejs.org/download:
一旦你有了Node.js,下一步就是安装TypeScript。确保您安装的版本至少是1.7或更高版本。运行下面的命令,安装1.5版本: $ npm install -g 'TypeScript@^1.7.3'
示例项目现在,你的环境已准备好了,让我们开始写第一个Angular2应用! 打开随这本书下载的代码并解压。在你的命令行中,通过cd进入 $ cd first_app/angular2-reddit-base
首先让我们先使用npm安装所有依赖 $ npm install 在项目的根目录下创建一个新的index.html文件,并添加一些基本HTML结构: <!doctype html>
<html>
<head>
<title>Angular 2 - Simple Reddit</title>
</head>
<body>
</body>
</html> 你的
Angular 2本身是一个JavaScript文件。所以我们需要一个script标签来引入它。并且还需引入一些Angular/TypeScript依赖的文件: Angular的依赖
Angular 2依赖于这四个库:
在你的 <script src="node_modules/es6-shim/es6-shim.js"></script>
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
ES6 ShimES Shim 为旧版的Javascript引擎提供了ECMAScript 6的行为,该Shim对于较新新版本的Safari,Chrome等并不严格需要,但是对于旧版本的IE是需要的。
Angular 2 Polyfill像ES6 Shim, angular2-polyfills提供跨浏览器的一些基本的标准化。 angular2-polyfills包含的代码专门用于zone,promise和reflection,如果你不知道这些东西是什么,你也不必担心。 SystemJSSystemJS是一个模块加载器。它帮助我们创建模块和解决模块之间依赖,模块加载在浏览器端的JavaScript是出奇的复杂,SystemJS使得过程变得更加容易。 RxJSRxJS是一个库用于在Javascript中进行反应式编程,一般来说,RxJS给了我们使用Observables的工具,用于发出的数据流。Angular 在许多地方使用了Observables,如在处理异步代码(例如: HTTP请求) 我们会在本书的RxJS这章讨论更多关于RxJS的内容,虽然在本章中它不是严格需要的,但值得一提的,你会在项目中经常使用它。 加载所有依赖现在我们已经添加了所有的依赖,我们的index.html看起来应该是这样的 <!doctype html>
<html>
<head>
<title>Angular 2 - Simple Reddit</title>
<!-- Libraries -->
<script src="node_modules/es6-shim/es6-shim.js"></script>
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
</head>
<body>
</body>
</html> 添加CSS我们也想添加一些CSS样式,使我们的应用不是完全无样式。让我们导入两个样式表: <!doctype html>
<html>
<head>
<title>Angular 2 - Simple Reddit</title>
<!-- Libraries -->
<script src="node_modules/es6-shim/es6-shim.js"></script>
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
<!-- Stylesheet -->
<link rel="stylesheet" type="text/css"
href="resources/vendor/semantic.min.css">
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
</body>
</html>
|
我们的第一个Typescript现在创建我们第一个TypeScript文件,在同级目录下添加一个叫app.ts的文件,并添加些代码:
code/first_app/hello-world/app.ts import { bootstrap } from "angular2/platform/browser";
import { Component } from "angular2/core";
@Component({
selector: 'hello-world',
template: `
<div>
Hello world
</div>
`
})
class HelloWorld { }
bootstrap(HelloWorld); 这段代码可能看起来完全看不懂,但别担心。我们会一步步解释。
我们从 同样我们从模块 注意,这个import语句的结构格式是 创建一个组件组件是Angular 2其背后一个很大的想法之一。 在我们的Angular应用中编写HTML标签来成为我们的交互式应用程序、但是浏览器只认识那些内置的标签,如 这背后就是组件的想法。我们教浏览器来认识新功能的新标签。
来创建我们第一个组件.当我们编写完这个组件后,我们可以在HTML中这样使用它 <hello-world></hello-world> 那么,我们如何定义一个新的组件?一个基本组件有二个部分:
如果你已经有一段时间的JavaScript编程经验,当看到下面的JavaScript会有点奇怪: @Component({
//...
}) 这里发生了什么事?如果你有Java背景,它看起来你会很熟悉:他们是注解。 注解会作为元数据添加到您的代码里。当我们在 我们希望用 @Component({
selector: 'hello-world'
}) 如果你熟悉CSS选择器,XPath,jQuery选择器等,你就知道有很多方法来配置一个 这里的 添加模板我们可以通过 @Component({
selector: 'hello-world',
template:`
<div>
Hello World
</div>
`
}) 请注意,模板字符串定义在我们的反引号(
引导我们的应用我们文件的最后行 一旦被引导,在 加载我们的应用要运行我们的应用程序,我们需要做2件事情:
将以下添加到body部分: <!doctype html>
<html>
<head>
<title>First App - Hello world</title>
<!-- Libraries -->
<script src="node_modules/es6-shim/es6-shim.js"></script>
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script> <script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
<!-- Stylesheet -->
<link rel="stylesheet" type="text/css" href="resources/vendor/semantic.min.css">
<link rel="stylesheet" type="text/css" href="styles.css"> </head>
<body>
<script>
System.config({
packages: {
app: {
format: 'register',
defaultExtension: 'js'
} }
});
System.import('app.js')
.then(null, console.error.bind(console));
</script>
<hello-world></hello-world>
</body>
</html> 在 System.import('app.js') 这行告诉 |
运行应用编译TypeScript代码为.js我们使用TypeScript编写我们的应用,我们有一个app.ts文件,下一步是将这个文件编译成Javascript,让我们的浏览器可以理解。 为了做到这一点,让我们运行TypeScript编译器的命令行,称为tsc: tsc 如果你得到一个没有错误信息的提示,这意味着编译成功,我们现在在同一目录下应该有app.js文件 ls app.js
# app.js should exist
使用npm如果你在tsc命令上面工作,你也可以使用npm来编译文件,在package.json里包含了一些简单的代码,我们定义了一些快捷命令来帮助你编译 尝试运行: npm run tsc // compiles TypeScript code once and exits
npm run tsc:w // watches for changes and compiles on change 应用的server我们来测试应用还有一个步骤。我们需要一个webserver来运行测试应用。 npm run serve 打开你得浏览器访问http://localhost:8080
,如果一切运行正常,你应该看到以下内容:
任何改变自动编译我们将对我们的应用程序代码进行大量的修改。我们可以利用--watch选项,不必每次都运行tsc生成新的js代码。 tsc --watch
message TS6042: Compilation complete. Watching for file changes. 其实,这是很常见的,我们已经为其创建了一个快捷方式 1.文件改变后重新编译 npm run go 现在,您可以编辑你的代码,变化将会自动在浏览器中体现出来。 |
将数据添加到组件我们的组件现在不是很有趣。大多数组件将具有动态数据。 让我们把name作为组件部分的一个新属性。这样,我们可以重用相同的组件,用于不同的输入。 作出以下修改: @Component({
selector: 'hello-world',
template: `<div>Hello {{ name }}</div>`
})
class HelloWorld {
name: string;
constructor() {
this.name = 'Felipe';
}
} 在这里我们做了三个改变: 1.name 属性 在 属性的类型是由TypeScript提供的特性。这将在我们的 2.一个 在HelloWorld类中我们定义了 在我们的构造函数中,可以通过使用this.name 给name属性赋值。 当我们写成:
我们可以理解为,每当创建一个新的 3.模板变量 在视图上我们添加了一个新的语法:{{ name }}, 这对大括号叫做模板标记(template-tags),模板标记之间的任何内容都将被扩展为表达式。在这里,因为组件绑定了我们的视图, 试试看尝试这些更改后,重新加载页面。我们应该看到“Hello Felipe” |
使用数组现在我们有一个 如果之前你用过Angular 1,你会使用ng-repeat指令,在Angular 2中,这个相似的指令名为NgFor, 让我们app.ts代码进行如下变化: import { bootstrap } from "angular2/platform/browser";
import { Component } from "angular2/core";
import { NgFor } from "angular2/common";
@Component({
selector: 'hello-world',
template: `
<ul>
<li *ngFor="#name of names">Hello {{ name }}</li>
</ul>
`
})
class HelloWorld {
names: string[];
constructor() {
this.names = ['Ari', 'Carlos', 'Felipe', 'Nate'];
}
}
bootstrap(HelloWorld); 第一个指出的变化是在我们的 我们改变类中的 接下去改变的是我们的模板,我们现在有一个
值 该NgFor指令会渲染names数组中的每一个元素产生一个新的li,每个li都会产生一个局部的name变量,这个变量会替换模板里的{{ name }},渲染到页面
当你重新加载页面,你可以看到我们数组中的每一个字符串: |
扩大我们的应用现在我们知道如何创建一个组件的基础部分,让我们重新审视我们的Reddit。在我们开始编码之前,先看看我们的应用,一个好的主意是将它分解成一个个单一逻辑组件。 我们将在这个应用程序中,使用两个组件:
应用组件让我们开始构建顶层应用组件,这个组件将会 1.存储我们目前的文章列表 我们要建立一个组件来代表我们整个应用:一个RedditApp组件。 为了做到这一点,我们将创建一个模板,一个新的组件:
import { bootstrap } from 'angular2/platform/browser'; import { Component } from 'angular2/core';
@Component({
selector: 'reddit',
template: `
<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> `
})
class RedditApp {
constructor() {
} }
bootstrap(RedditApp); 在这里我们申明了一个RedditApp组件,我们的selector是reddit,意味着这个组件会将 我们创建了的模板定义了两个input,一个是文章的标题,一个是文章的链接地址 我们需要使用新的RedditApp组件,需要将index.html里的 当您重新加载浏览器,你应该可以看到表单被渲染: 添加交互现在我们在表单中有input标签了,但我们没有任何的方式来提交数据。让我们通过在表单中添加一个提交按钮来增加一些交互: @Component({
selector: 'reddit',
template: `
<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" #newtitle>
</div>
<div class="field">
<label for="link">Link:</label>
<input name="link" #newlink>
</div>
<button (click)="addArticle(newtitle, newlink)"
class="ui positive right floated button">
Submit link
</button>
</form>
` })
class RedditApp {
constructor() {
}
addArticle(title: HTMLInputElement, link: HTMLInputElement): void {
console.log(`Adding article title: ${title.value} and link: ${link.value}`);
}
} 注意我们已经做了4个变化
让我们以相反的顺序来看看每一个步骤: 为input绑定一个局部变量请注意下面是我们的第一个input <input name="title" #newtitle>
同样我们为另一个input标签添加一个 绑定事件在我们的button按钮上添加了一个(click)属性来定义了点击事件,当button被点击时,会调用addArticle方法,addArticle方法有2个参数newtitle和newlink.这些东西是从哪里来的?
所有在一起: <button (click)="addArticle(newtitle, newlink)" class="ui positive right floated button">
Submit Link
</button> 定义action逻辑在 addArticle(title: HTMLInputElement, link: HTMLInputElement):void {
console.log(`Adding article title: ${title.value} and link: ${link.value}`);
} 尝试执行它!现在,当你点击提交按钮,你可以看到,该消息在控制台上打印出来: 添加文章组件现在我们有一个发布新文章的组件,但我们没有在任何地方展示新的文章。 因为每一篇文章提交将在页面上显示为一个列表,这是一个新的组件的最佳人选。 让我们创建一个新的组件来显示提交的文章。 为此,我们在同一文件内创建一个新的组件,将下面的代码在RedditApp组件中加上 @Component({
selector: 'reddit-article',
host: {
class: 'row'
},
template: `
<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>
`
})
class ArticleComponent {
votes:number;
title:string;
link:string;
constructor() {
this.votes = 10;
this.title = 'Angular 2';
this.link = 'http://angular.io';
}
voteUp() {
this.votes += 1;
}
voteDown() {
this.votes -= 1;
}
} 请注意,我们有三个部分来定义这个新组件:
让我们来说说每个部分: 创建reddit-article组件 @Component({
selector: 'reddit-article',
host: {
class: 'row'
}, 首先,我们通过@component注解来定义新的组件, 因此,使用该组件的最重要的方法是将下列标记放置在标记中: <reddit-article>
</reddit-article> 当页面渲染时,这些标记将留在我们的视图中。 我们希望每个 在Angular 2中,一个组件的host表示该组件元素,你会注意到,将
创建reddit-artcile模板 然后我们通过 template: `
<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>
` 在这里有很多的标签,让我们把它分解: 我们有2列 1.投票数在左边 我们在模板中显示votes和title使用模板语法{{ votes }} 和 {{ title }}.这2个值将使用ArticleComponent类中的votes和title属性来渲染 我们也可以将模板语法使用在属性内。如a标签的 我们的 创建reddit-article的ArticleComponent定义类 最后,我们创建ArticleComponent定义类: class ArticleComponent {
votes:number;
title:string;
link:string;
constructor() {
this.votes = 10;
this.title = 'Angular 2';
this.link = 'http://angular.io';
}
voteUp() {
this.votes += 1;
}
voteDown() {
this.votes -= 1;
}
} 该ArticleComponent类中有3个属性
在controlstor()我们设置下属性 constructor(){
this.votes = 10;
this.title = 'Angular 2';
this.link = 'http://angular.io';
} 并且我们对于投票也定义了2个方法, voteUp() {
this.votes += 1;
}
voteDown() {
this.votes -= 1;
} voteUp中我们让 使用reddit-article组件 为了使用该组件,使数据可见,在需要使用的地方添加上 在本例中,我们想RedditApp组件中使用这个新组件,让我们改变该组件代码。首先我们需要在RedditApp模板的··标签后加上 <button (click)="addArticle(newtitle, newlink)" class="ui positive right floated button">
Submit link
</button>
</form>
<div class="ui grid posts">
<reddit-article>
</reddit-article>
</div>
` 让我们重新加载下浏览器,我们会看到 每当碰到这种问题,第一件事就是打开你的浏览器的开发者控制台。如果我们检查标签(见下面的截图),我们可以看到, 这标签未被渲染是因为RedditApp组件不知道ArticleComponent组件是什么。
为了告诉RedditApp关于新ArticleComponent组件,我们需要在RedditApp指令中添加属性 // for RedditApp
@Component({
selector: 'reddit',
directives: [ArticleComponent],
template: `
// ... 现在,我们重新加载浏览器,我们应该看到文章被正确渲染了: 不过,如果你现在点击voteUp 和 voteDown链接,你会看到页面竟然被重新加载 这是因为,javascript的默认情况下,点击事件会冒泡到所有的父组件上,因为点击事件被传播给父元素上,我们的浏览器试图访问空的链接。 要解决这个错误,我们只需使click事件处理程序中返回一个false。这将确保浏览器不会尝试刷新页面。来改变我们的代码: voteUp() {
this.votes += 1;
return false;
}
voteDown() {
this.votes -= 1;
return false;
} 现在,如果你点击链接,你会看到投票的增加和减少。 |
渲染多行现在我们在这个页面只有一篇文章,没有办法渲染更多文章,除非我们创建新的 创建Article类一个较好的做法是,当编写Angular2代码时试图从你的组件代码中独立出你的数据结构,为了实现这点,做任何进一步的更改组件之前,让我们创建将代表文章的数据结构,在 class Article {
title: string;
link: string;
votes: number;
constructor(title, link) {
this.title = title;
this.link = link;
this.votes = 0;
}
} 在这里我们创建一个表示Artcile的类,注意这只是一个普通的类,并不是一个组件,在MVC模式中它属于model 每篇文章有一个title,link和一个总的votes,当我们创建一个新的文章时,我们需要title和link,我们还假设默认的votes是0 现在在ArticleComponent代码中使用我们新的Article类,而不是直接在ArticleComponent组件存储的article的内容, class ArticleComponent {
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;
}
} 注意:现在在组件上已经不直接保存我们的title,link和votes,而是保存一个article的引用 当涉及到voteUp(和voteDown),我们不该增减组件上得vote属性,而是应该增减article上得vote属性 这个重构带来了另一个变化:我们需要更新我们的视图,从正确的位置获得模板变量。为了做到这一点,我们需要改变我们的模板标签。也就是说,在之前使用{{votes}},我们需要将其更改为{{article.votes}}: template: `
<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方法会直接修改article内部的属性 问题是,我们的ArticleComponent组件知道太多关于Article类的内部。为了解决这个问题,让我们为Article也添加相应方法,ArticleComponent也要做出相应修改: 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;
} 然后我们改变ArticleComponent来调用这些方法: class ArticleComponent {
article: Article;
voteUp(): boolean {
this.article.voteUp();
return false;
}
voteDown(): boolean {
this.article.voteDown();
return false;
}
}
当我们重新加载浏览器后,你会注意到所有的显示都是一样的,但我们现在有更清晰的代码。 存储多个文章让我们写一个允许我们有多篇文章的代码。 改变RedditApp的属性,创建一个articles集合 class RedditApp {
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): void { 注意我们RedditApp的这行 articles: Article[]; 如果你不用TypeScript, 我们可以在构造函数设置this.articles这个列表: 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),
];
} 配置ArticleComponent组件的inputs属性现在,我们已经创建了一个article model,我们怎样才能将他们交给ArticleComponent组件用呢? 在这里,我们引入了一个新的组件属性称为inputs。我们可以配置组件的inputs属性,它会接收从父传递进来数据。 以前我们的ArticleComponent组件类定义成这样的: class ArticleComponent {
article: Article;
constructor() {
this.article = new Article('Angular 2', 'http://angular.io');
}
} 这里的问题是,我们已经将特定文章硬编码在构造函数中,制作组件的要点不仅是封装,而且还具有可重用性。 我们真正喜欢做的是配置我们想要显示的文章。如果,例如,我们有两篇文章,文章1和文章2,我们希望能够通过article作为一个"参数"来传递给reddit-article组件: <reddit-article [article]="article1"></reddit-article>
<reddit-article [article]="article2"></reddit-article> Angular 允许我们这样做,通过使用 @Component({
selector: 'reddit-article',
inputs: ['article'],
// ... same
})
class ArticleComponent {
article: Article;
//... 现在如果我们有一篇文章在变量myArticle里,我们可以在ArticleComponent的view里这样写 <reddit-article [article]="myArticle"></reddit-article> 请注意这里的语法:把在input的名称放在[]内,像这样:[article]属性的值就是我们要传递给该input的内容 然后,这是很重要的,在ArticleComponent实例上的 注意 inputs是一个数组,这是因为你可以指定一个组件有许多inputs。 所以我们ArticleComponent完整的代码看起来像这样: @Component({
selector: 'reddit-article',
inputs: ['article'],
host: {
class: 'row'
},
template: `
<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>
`
})
class ArticleComponent {
article:Article;
voteUp():boolean {
this.article.voteUp();
return false;
}
voteDown():boolean {
this.article.voteDown();
return false;
}
} 渲染文章列表早些时候我们配置RedditApp来存储articles数组,现在来配置RedditApp来渲染所有的文章。要做到不只有一个 添加这些到RedditApp @component的template内的 Submit link
</button>
</form>
<!-- start adding here -->
<div class="ui grid posts">
<reddit-article
*ngFor="#article of articles"
[article]="article">
</reddit-article>
</div>
<!-- end adding here --> 还记得我们在前面章节使用
我们使用
如果你现在重新加载你的浏览器,你可以看到所有文章将被渲染: |
添加新文章现在我们需要改变 addArticle(title: HTMLInputElement, link: HTMLInputElement): void {
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 = '';
} 这将:
如果你点击submit添加一篇新的article,你会在列表中看到这篇新加的文章 |
收尾让我们添加一个功能,显示用户点击该链接当会被重定向到的域名 添加domain方法到Article类: domain(): string {
try {
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()"> 现在当我们重新加载浏览器时,应该看到每个网址的域名。 基于评分的排序如果你点击投票,你会发现有一些不太正确:我们的文章不按评分排序!我们绝对希望看到的最高评分的文章。 我们将文章存储在RedditApp的articles数组里,但数组是未排序的,最简单的方法是在RedditApp上创建一个新的方法sortedArticles sortedArticles(): Article[] {
return this.articles.sort((a: Article, b: Article) => b.votes - a.votes);
} 现在我们可以使用ngFor来遍历我们的 <div class="ui grid posts">
<reddit-article *ngFor="#article of sortedArticles()" [article]="article">
</reddit-article>
</div> |
完整的代码import { bootstrap } from 'angular2/platform/browser';
import { Component } from 'angular2/core';
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;
}
domain(): string {
try {
const link: string = this.link.split('//')[1];
return link.split('/')[0];
} catch (err) {
return null;
}
}
voteUp(): void {
this.votes += 1;
}
voteDown(): void {
this.votes -= 1;
}
}
@Component({
selector: 'reddit-article',
inputs: ['article'],
host: {
class: 'row'
},
template: `
<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>
<div class="meta">({{ article.domain() }})</div>
<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>
`
})
class ArticleComponent {
article: Article;
voteUp(): boolean {
this.article.voteUp();
return false;
}
voteDown(): boolean {
this.article.voteDown();
return false;
}
}
@Component({
selector: 'reddit',
directives: [ArticleComponent],
template: `
<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" #newtitle>
</div>
<div class="field">
<label for="link">Link:</label>
<input name="link" #newlink>
</div>
<button (click)="addArticle(newtitle, newlink)"
class="ui positive right floated button">
Submit link
</button>
</form>
<div class="ui grid posts">
<reddit-article
*ngFor="#article of sortedArticles()"
[article]="article">
</reddit-article>
</div>
`
})
class RedditApp {
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): void {
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 = '';
}
sortedArticles(): Article[] {
return this.articles.sort((a: Article, b: Article) => b.votes - a.votes);
}
}
bootstrap(RedditApp); |
结束语我们成功了,我们创建了第一个Angular 2应用,我们在后面还会学习更多,理解数据流,使用ajax,组件的创建,路由,操纵DOM等。 但现在,请享受你的成功!大部分的Angular 2应用仅仅是象我们上面那样: 1.分解你得应用成为组件 |
寻求帮助这一章你有什么麻烦吗?你有没有发现一个bug或者有不能运行的代码?我们很乐意听到您的声音!
|
该issue关闭讨论,如有问题请去 #43 提问
目录
The text was updated successfully, but these errors were encountered: