由于官网给出JavaScript例子不是很完整,此文主要简单的记录用JavaScript写Angular 2 的Hello World程序。
用javascript实现官网英雄指南教程
本文参照官网实例地址:TypeScript实现的英雄编辑器教程-路由
保持项目运行:在项目目录下执行npm start
1.本章结束后项目文件目录
angular-quickstart
|---app
| |
| |---app.component.css
| |---app.component.js
| |---app.module.js
| |---app-routing.module.js
| |---dashboard.component.css
| |---dashboard.component.html
| |---dashboard.component.js
| |---hero.js
| |---hero.service.js
| |---hero-detail.component.css
| |---hero-detail.component.html
| |---hero-detail.component.js
| |---heroes.component.css
| |---heroes.component.html
| |---heroes.component.js
| |---main.js
| |---mock-heroes.js
|
|---node_modules ...
|---package.json
|---systemjs.config.js
|---index.html
|---style.css
2.路由及导航-代码重构
AppComponent改写成应用程序的外壳,只处理导航,将其他功能转移到各自功能拆分到各自的组件内。
将APPComponent中的 功能移动至HeroesComponent。
可以将app.component.js复制改名为heroes.component.js
将AppComponent改名为HeroesComponent
将选择器selector从’my-app’改为’my-heroes’
修改后app/heroes.component.js片段
var ng_core = require('@angular/core');
var Hero = require('./hero').Hero;
var HeroService = require('./hero.service').HeroService;
var HeroesComponent = (function(){
function HeroesComponent (){
}
HeroesComponent = ng_core.Component({
selector: 'my-heroes',
template:`
<h2>My Heroes</h2>
<ul class="heroes">
<li *ngFor="let hero of heroes" (click)="onSelect(hero)" [class.selected]="hero === selectedHero">
<span class="badge">{{hero.id}}</span> {{hero.name}}
</li>
</ul>
<my-hero-detail [hero]="selectedHero"></my-hero-detail>
`
}).Class({
constructor:[HeroService,function(heroService) {
this.hero = new Hero(1,'Windstorm');
this.heroService = heroService;
//this.heroes = heroService.getHeroes();//HEROES;
this.selectedHero = null;
this.onSelect = function(hero){
this.selectedHero = hero;
};
}]
});
HeroesComponent.prototype.getHeroes = function(){
return this.heroService.getHeroes();
};
HeroesComponent.prototype.ngOnInit = function(){
this.heroes = this.getHeroes();
};
return HeroesComponent;
}());
exports.HeroesComponent = HeroesComponent;
重写app.component.js为入口
var ng_core = require('@angular/core');
var AppComponent = (function(){
function AppComponent (){
}
AppComponent = ng_core.Component({
selector: 'my-app',
template:`
<h1>{{title}}</h1>
<my-heroes></my-heroes>
`
}).Class({
constructor:function() {
this.title = 'Tour of Heroes';
}
});
return AppComponent;
}());
exports.AppComponent = AppComponent;
新建app/app.module.js
var ng_core = require('@angular/core');
var BrowserModule = require('@angular/platform-browser').BrowserModule;
var FormsModule = require('@angular/forms').FormsModule;
var AppComponent = require('./app.component').AppComponent;
var HeroDetailComponent = require('./hero-detail.component').HeroDetailComponent;
var HeroesComponent = require('./heroes.component').HeroesComponent;
var HeroService = require('./hero.service').HeroService;
var AppModule = (function(){
function AppModule(){
};
AppModule = ng_core.NgModule({
imports: [ BrowserModule,FormsModule],
declarations: [ AppComponent,HeroDetailComponent,HeroesComponent],
bootstrap: [ AppComponent ],
providers:[HeroService]
})
.Class({
constructor: function() {}
});
return AppModule;
}());
exports.AppModule = AppModule;
将HeroService提供到跟模块,从HeroesComponent移除,这样HeroService可以被多个视图进行共用。至此重构完成,代码重构后,与之前运行效果一致。
3.添加路由
在index.html中的head标签内添加标签,请将该标签加在head标签最开始,为相对路径提供根路径
在app/app.module.js中添加路由
var ng_core = require('@angular/core');
var BrowserModule = require('@angular/platform-browser').BrowserModule;
var FormsModule = require('@angular/forms').FormsModule;
var RouterModule = require('@angular/router').RouterModule;
var AppComponent = require('./app.component').AppComponent;
var HeroDetailComponent = require('./hero-detail.component').HeroDetailComponent;
var HeroesComponent = require('./heroes.component').HeroesComponent;
var HeroService = require('./hero.service').HeroService;
var AppModule = (function(){
function AppModule(){
};
AppModule = ng_core.NgModule({
imports: [
BrowserModule,
FormsModule,
RouterModule.forRoot([
{
path:'heroes',
component:HeroesComponent
}
])
],
declarations: [ AppComponent,HeroDetailComponent,HeroesComponent],
bootstrap: [ AppComponent ],
providers:[HeroService]
})
.Class({
constructor: function() {}
});
return AppModule;
}());
exports.AppModule = AppModule;
修改app.component.js的template
template:`
<h1>{{title}}</h1>
<a routerLink="/heroes">Heroes</a>
<router-outlet></router-outlet>
`
点击a标签的Heroes,会将根据routerLink属性将该视图导航到/heroes,并且将该视图在router-outlet内显示
4.添加新组件-控制台
新建app/dashboard.component.js
var Component = require('@angular/core').Component;
var DashboardComponent = (function(){
var DashboardComponent = function(){
};
DashboardComponent = Component({
selector: 'my-dashboard',
template: '<h3>My Dashboard</h3>'
}).Class({
constructor:function() {
}
});
return DashboardComponent;
}());
exports.DashboardComponent = DashboardComponent;
在app/app.module.js中引入DashbordComponent,并且添加DashbordComponent组件到路由,并且如果未输入url,将默认重定向到DashboardComponent
app/app.module.js
var ng_core = require('@angular/core');
var BrowserModule = require('@angular/platform-browser').BrowserModule;
var FormsModule = require('@angular/forms').FormsModule;
var RouterModule = require('@angular/router').RouterModule;
var AppComponent = require('./app.component').AppComponent;
var HeroDetailComponent = require('./hero-detail.component').HeroDetailComponent;
var HeroesComponent = require('./heroes.component').HeroesComponent;
var HeroService = require('./hero.service').HeroService;
var DashboardComponent = require('./dashboard.component.js').DashboardComponent;
var AppModule = (function(){
function AppModule(){
};
AppModule = ng_core.NgModule({
imports: [
BrowserModule,
FormsModule,
RouterModule.forRoot([
{
path:'heroes',
component:HeroesComponent
},{
path: 'dashboard',
component: DashboardComponent
},{
path: '',
redirectTo: '/dashboard',
pathMatch: 'full'
}
])
],
declarations: [ AppComponent,HeroDetailComponent,HeroesComponent,DashboardComponent],
bootstrap: [ AppComponent ],
providers:[HeroService]
})
.Class({
constructor: function() {}
});
return AppModule;
}());
exports.AppModule = AppModule;
修改app/app.component.js模板,可以切换视图
template:`
<h1>{{title}}</h1>
<nav>
<a routerLink="/dashboard">Dashboard</a>
<a routerLink="/heroes">Heroes</a>
</nav>
<router-outlet></router-outlet>
`
5.完善控制台
在控制台内,显示4个顶级英雄
将template提取到app/dashboard.component.html
<h3>Top Heroes</h3>
<div class="grid grid-pad">
<div *ngFor="let hero of heroes" class="col-1-4">
<div class="module hero">
<h4>{{hero.name}}</h4>
</div>
</div>
</div>
在app/dashbord.component.js中引用该文件,如果填写相对路径, 则此时需要systemjs提供CommonJs表中,便可以使用module.id来支持相对路径。
将HeroService注入到DashboardComponent,并选取第2、第3、第4、第5这几个英雄作为DashboardComponent的heroes
var Component = require('@angular/core').Component;
var HeroService = require('./hero.service').HeroService;
var DashboardComponent = (function(){
var DashboardComponent = function(){
};
DashboardComponent = Component({
moduleId: module.id,
selector: 'my-dashboard',
templateUrl: 'dashboard.component.html'
}).Class({
constructor:[HeroService,function(heroService) {
this.heroService = heroService;
}]
});
DashboardComponent.prototype.getHeroes = function(){
return this.heroService.getHeroes();
};
DashboardComponent.prototype.ngOnInit = function(){
this.heroes = this.getHeroes().slice(1, 5);
};
return DashboardComponent;
}());
exports.DashboardComponent = DashboardComponent;
6.路由到英雄详情页
修改app.module.js路由,添加如下内容
,{
path: 'detail/:id',
component: HeroDetailComponent
}
:id为占位符,可以通过占位符传递id
修改HeroDetailComponent,通过导航获取英雄id
向HeroDetailComponent中注入ActivatedRoute,Location,HeroService组件,并存储到HeroDetailComponent对象内。
引入需要的组件
var ActivatedRoute = require('@angular/router').ActivatedRoute;
var Location = require('@angular/common').Location;
var HeroService = require('./hero.service').HeroService;
HeroDetailComponent的构造函数
constructor:[HeroService,ActivatedRoute,Location,function(heroService,activatedRoute,location){
this.heroService = heroService;
this.route = activatedRoute;
this.location = location;
}]
实现 ngOnInit接口,回退方法
HeroDetailComponent.prototype.ngOnInit = function(){
var params = this.route.params.value;
var id = parseInt(params['id']); //获取url传递过来的id
var hero = this.heroService.getHero(id);
this.hero = hero;
}
HeroDetailComponent.prototype.goBack = function(){
this.location.back(); //回退到上一视图
}
修改template,并将该模板存储到app/hero-detail.component.html
<div *ngIf="hero">
<h2>{{hero.name}} details!</h2>
<div>
<label>id: </label>{{hero.id}}</div>
<div>
<label>name: </label>
<input [(ngModel)]="hero.name" placeholder="name" />
</div>
<button (click)="goBack()">Back</button>
</div>
修改HeroDetailComponent组件元数据
HeroDetailComponent = ng_core.Component({
moduleId: module.id,
selector: 'my-hero-detail',
templateUrl:'hero-detail.component.html',
inputs:['hero'],
}
在 HeroService中添加getHero方法
app/hero.service.js
HeroService.prototype.getHero = function(id){
for(var i in HEROES){
var hero = HEROES[i];
if(hero.id === id){
return hero;
}
}
}
修改app/dashboard.component.html文件
app/dashboard.component.html
<h3>Top Heroes</h3>
<div class="grid grid-pad">
<a *ngFor="let hero of heroes" [routerLink]="['/detail', hero.id]" class="col-1-4">
<div class="module hero">
<h4>{{hero.name}}</h4>
</div>
</a>
</div>
至此,在dashboard视图下,点击以为英雄,将被导航至英雄详情页。
7.重构路由,新建路由模块
新建app/app-routing.module.js
var ng_core = require('@angular/core');
var RouterModule = require('@angular/router').RouterModule;
var HeroDetailComponent = require('./hero-detail.component').HeroDetailComponent;
var HeroesComponent = require('./heroes.component').HeroesComponent;
var DashboardComponent = require('./dashboard.component.js').DashboardComponent;
var AppRoutingModule = (function(){
var routes = [
{
path: '',
redirectTo: '/dashboard',
pathMatch: 'full'
},
{
path:'heroes',
component:HeroesComponent
},
{
path: 'dashboard',
component: DashboardComponent
},
{
path: 'detail/:id',
component: HeroDetailComponent
}
];
var AppRoutingModule = function AppRoutingModule(){
};
AppRoutingModule = ng_core.NgModule({
imports: [ RouterModule.forRoot(routes)],
exports:[RouterModule]
})
.Class({
constructor: function() {}
});
return AppRoutingModule;
}());
exports.AppRoutingModule = AppRoutingModule;
重写app/app.module.js
var ng_core = require('@angular/core');
var BrowserModule = require('@angular/platform-browser').BrowserModule;
var FormsModule = require('@angular/forms').FormsModule;
var AppComponent = require('./app.component').AppComponent;
var HeroDetailComponent = require('./hero-detail.component').HeroDetailComponent;
var HeroesComponent = require('./heroes.component').HeroesComponent;
var HeroService = require('./hero.service').HeroService;
var DashboardComponent = require('./dashboard.component').DashboardComponent;
var AppRoutingModule = require('./app-routing.module').AppRoutingModule;
var AppModule = (function(){
function AppModule(){
};
AppModule = ng_core.NgModule({
imports: [
BrowserModule,
FormsModule,
AppRoutingModule
],
declarations: [ AppComponent,HeroDetailComponent,HeroesComponent,DashboardComponent],
bootstrap: [ AppComponent ],
providers:[HeroService]
})
.Class({
constructor: function() {}
});
return AppModule;
}());
exports.AppModule = AppModule;
8.重构HeroesComponent
重构HeroesComponent的template,并将其拆入app/uppercase.html、将style拆入到app/heroes.component.css
其中| uppercase为管道,可以将表示格式化显示为大写字母
<h2>My Heroes</h2>
<ul class="heroes">
<li *ngFor="let hero of heroes" (click)="onSelect(hero)" [class.selected]="hero === selectedHero">
<span class="badge">{{hero.id}}</span> {{hero.name}}
</li>
</ul>
<!--<my-hero-detail [hero]="selectedHero"></my-hero-detail>-->
<div *ngIf="selectedHero">
<h2>
{{selectedHero.name | uppercase}} is my hero //new
</h2>
<button (click)="gotoDetail()">View Details</button>
</div>
重写app/heroes.component.js
var ng_core = require('@angular/core');
var Router = require('@angular/router').Router;
var Hero = require('./hero').Hero;
var HeroService = require('./hero.service').HeroService;
var HeroesComponent = (function(){
function HeroesComponent (){
}
HeroesComponent = ng_core.Component({
moduleId: module.id,
selector: 'my-heroes',
templateUrl:'heroes.component.html',
styleUrls: ['heroes.component.css']
}).Class({
constructor:[HeroService,Router,function(heroService,router) {
this.hero = new Hero(1,'Windstorm');
this.heroService = heroService;
this.router = router;
this.selectedHero = null;
this.onSelect = function(hero){
this.selectedHero = hero;
};
}]
});
HeroesComponent.prototype.getHeroes = function(){
return this.heroService.getHeroes();
};
HeroesComponent.prototype.ngOnInit = function(){
this.heroes = this.getHeroes();
};
HeroesComponent.prototype.gotoDetail = function(){
this.router.navigate(['/detail', this.selectedHero.id]);
}
return HeroesComponent;
}());
exports.HeroesComponent = HeroesComponent;
9.美化
美化DashboardComponent,添加元数据styleUrls: [ ‘dashboard.component.css’ ]
新增app/dashboard.component.css
[class*='col-'] {
float: left;
padding-right: 20px;
padding-bottom: 20px;
}
[class*='col-']:last-of-type {
padding-right: 0;
}
a {
text-decoration: none;
}
*, *:after, *:before {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
h3 {
text-align: center; margin-bottom: 0;
}
h4 {
position: relative;
}
.grid {
margin: 0;
}
.col-1-4 {
width: 25%;
}
.module {
padding: 20px;
text-align: center;
color: #eee;
max-height: 120px;
min-width: 120px;
background-color: #607D8B;
border-radius: 2px;
}
.module:hover {
background-color: #EEE;
cursor: pointer;
color: #607d8b;
}
.grid-pad {
padding: 10px 0;
}
.grid-pad > [class*='col-']:last-of-type {
padding-right: 20px;
}
@media (max-width: 600px) {
.module {
font-size: 10px;
max-height: 75px; }
}
@media (max-width: 1024px) {
.grid {
margin: 0;
}
.module {
min-width: 60px;
}
}
美化HeroDetailComponent,添加元数据styleUrls: [ ‘hero-detail.component.css’ ],
新增app/hero-detail.component.css
label {
display: inline-block;
width: 3em;
margin: .5em 0;
color: #607D8B;
font-weight: bold;
}
input {
height: 2em;
font-size: 1em;
padding-left: .4em;
}
button {
margin-top: 20px;
font-family: Arial;
background-color: #eee;
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer; cursor: hand;
}
button:hover {
background-color: #cfd8dc;
}
button:disabled {
background-color: #eee;
color: #ccc;
cursor: auto;
}
美化AppComponent,添加源数据styleUrls: [‘app.component.css’],
新增app/app.component.css
h1 {
font-size: 1.2em;
color: #999;
margin-bottom: 0;
}
h2 {
font-size: 2em;
margin-top: 0;
padding-top: 0;
}
nav a {
padding: 5px 10px;
text-decoration: none;
margin-top: 10px;
display: inline-block;
background-color: #eee;
border-radius: 4px;
}
nav a:visited, a:link {
color: #607D8B;
}
nav a:hover {
color: #039be5;
background-color: #CFD8DC;
}
nav a.active {
color: #039be5;
}
修改AppComponent的template,添加routerLinkActive属性,当连接激活时,添加样式
<a routerLink="/dashboard" routerLinkActive="active">Dashboard</a>
<a routerLink="/heroes" routerLinkActive="active">Heroes</a>
修改style.css
/* Master Styles */
h1 {
color: #369;
font-family: Arial, Helvetica, sans-serif;
font-size: 250%;
}
h2, h3 {
color: #444;
font-family: Arial, Helvetica, sans-serif;
font-weight: lighter;
}
body {
margin: 2em;
}
body, input[text], button {
color: #888;
font-family: Cambria, Georgia;
}
/* . . . */
/* everywhere else */
* {
font-family: Arial, Helvetica, sans-serif;
}
10.本章附件
由于本章文件太多,不一一贴出,请参考附件
备注:javascript初学,不懂太多。