这个demo,分为两个组件,组件1负责输入,组件2负责显示和投票。
两个组件本质上是对模型的修改,组件1负责输入模型数据,组件2通过模型输出显示数据,并且通过引入模型的增减函数,而模型2还需要导入从模型1导入input。
Expanding Application
创建新project ng new xx
导入CSS-framework, [Semantic-UI](http://semantic- ui.com/)
其他类似的有 Zurb Foundation or Twitter Bootstrap
npm install semantic-ui --save
cd semantic/
gulp build
好像不能直接在组件使用
可以使用主题,在app/vendor导入
应用组件构建
组件需要
1,保存目前文章列表
2,上传文章的表格
构建输入页面
在app.component.html中构建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>
添加按钮
点击触发addArticle
函数
<button (click)="addArticle()"
class="ui positive right floated button">
Submit link
</button>
在app.components中定义addArticle
函数
export class AppComponent {
addArticle(title: HTMLInputElement, link: HTMLInputElement): boolean {
console.log(`Adding article title: ${title.value} and link: ${link.value}`);
return false;
}
}
接收两个参数title
和link
,需要在template的input中添加对应的变量
用#表示局部变量
<input name="title" #newtitle>
...
<input name="link" #newlink>
...
<button (click)="addArticle(newtitle, newlink)"
- 在template, 创建button标签,添加click事件,触发函数a
- 在component, 定义函数a
- 回到template, 在button添加所需的函数变量
input
数据绑定,#newtitle
<input name="title" #newtitle>
表明input绑定变量 #newtitle
, #newtitle
语法上叫做解析(reslove),
意义是使newtitle
变量在这个视图层面可用。
现在 newtitle
是一个对象,代表了这个input DOM元素,类型是HTMLInputElement。
所以可以用newtitle.value
输出值。
把事件跟动作绑定 Binding actions to events
<button (click)="addArticle(newtitle, newlink)"
表示在button
标签中,当click
事件发生时,会触发addArticle
函数
addArticle
在组件AppComponent
export class
中被定义
newtitle
,newlink
来自于input
标签中对应的#newtitle
, #newtitle
解析
动作函数定义
addArticle(title: HTMLInputElement, link: HTMLInputElement): boolean {
console.log(`Adding article title: ${title.value} and link: ${link.value}`);
return false;
}
title
和link
是对象(HTMLInputElement类型),而不是等于input的值。
所以输出值必须用,title.value
, 比如${title.value}
目前只是打印输出值
目前在浏览器console中,可以输出值。
构建输出页面
把输入的值显示出来
ng generate component article
创建一个新component
构建articlecomponent.html
页面
目标页面
<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>
分数在左边,文章信息在右边
先设置左右两个div,左边div占 four wide column,右边 twelve wide column(来自SemanticUI)
设置{{ votes }}
,{{ title }}
和{{ href }}
数据来于ArticleComponent
同时定义两个click
按钮
定义component
export class ArticleComponent implements OnInit {
@HostBinding('attr.class') cssClass = 'row';
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() {
}
}
导入了 HostBinding
, 宿主绑定shuzhu,保持内部元素继承组件设置。
设置组件内部为 row
在AppComponent
导入app-article
<div class="ui grid posts">
<app-article>
</app-article>
</div>
如果页面没有出现app-article
检查浏览器elements 有没有加载,
如果有,但没有显示,可能是app.module.ts没有注册。 p68
注意点击上下按钮,会出现页面重新载入,也就是会到默认值10。
因为JS语言中,click事件会有逐层往上传播,比如div->body->html->document,
所以click事件会传输到所有父组件。浏览器会试图加载空连接。
所以要在voteDown
函数设置返回false
,就不会重载。
return false;
渲染多个Rowa
增加数据结构,用来保存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;
}
}
这是Model-view-Controller
中的Model
title
和votes
代表每一篇新文章的值
votes
为可选,默认为0
现在把model
引入到ArticleComponent
import { Article } from './article.model';
在export class中,导入模型article: Article;
this.article = new Article
设立新数据
this.article.votes += 1;
定位模型数据
export class ArticleComponent implements OnInit {
@HostBinding('attr.class') cssClass = 'row';
article: Article;
constructor() {
this.article = new Article(
'Angular 2',
'http://angular.io',
10);
}
voteUp() {
this.article.votes += 1;
return false;
}
voteDown() {
this.article.votes -= 1;
return false;
}
ngOnInit() {
}
}
同时需要在template中更改对应的模型位置,
比如{{ votes }}
改为 {{ article.votes }}
目前问题是:voteUp
和 voteDown
通过直接修改Article内部数据破坏了对Article的封装,
打破了迪米特法则(Law of Demeter):
法则基本上说:每一个单元应该尽可能跟其他单元保持独立关系,除非跟它有直接关系的。
所以把voteUp
和 voteDown
移到article模型内部,同时在模型中增加domian
函数
然后在组件内部引用
voteDown(): boolean {
this.article.voteDown();
return false;
}
为什么需要把voteUp
同时放在模型和组件中?
因为每一个class有不同的功能,组件负责view展示,而数据递交放在model。
功能区分,对于项目可以便于维护。
Fat Models, Skinny Controllers
多篇文章
在app.component 中 添加模型 p75
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),
];
}
现在要把app.component数据传递到article.component中
用 Input
指令传递
class ArticleComponent {
@Input() article: Article;
现在让AppComponent渲染所有文章,
<div class="ui grid posts">
<app-article
*ngFor="let article of articles"
[article]="article">
</app-article>
</div>
添加文章
在app.component中,p81
写入addArticle
方法
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;
}
1,创建一个文章,根据上传数据
2,增加到Article 模型中
3,清除input区域
加入url p82
根据之前的domain函数,负责把url拆解
在增加到ArtcileComponent的template中即可
分数排列
在app.component中添加排序函数
sortedArticles(): Article[] {
return this.articles.sort((a: Article, b: Article) => b.votes - a.votes); }
}
在app.component中使用
<div class="ui grid posts">
<app-article
*ngFor="let article of sortedArticles()" //用sortedArticles()替代Articles
[article]="article">
</app-article>
</div>
部署 Deployment
可分为如下步骤
- 把ts语言转化为js语言,浏览器可读
- 把js文件打包成一个或两个文件
- 上传js,HTML,CSS,和images文件
因为Angular程序本质是HTML文件加载JS语言。
build first app for production
使用ng build
ng build --target=production --base-href '/'
把应用建造成生产环境,默认主页是 /
可以设置为 /ng-book-demo/
会创建一个dist文件夹,里面是整个应用的完整打包
上传服务器
有很多方法,这个demo用的是 now
本质上是把dist
文件上传到网络
now快速部署js网站,每次部署一个项目都会得到一个独有的url
cd dist
now
输入email,即可得到输出网址