Angular 2.0 SPA應用 - 從腳手架開始 (1)

前言

今天是2017年3月7日,Angular 2.0目前最新版本是 2.0.0-beta.17。網絡上搜索到的Augular項目及實例,大部分都是1.x版本的,即使是使用2.x版本的Demo,由于寫作時間關系,部分的代碼顯得有些過時。
本系列希望通過一步步實現一個郵件發送應用,講解Angular 2.0中的Route、Module、Component、Service、Dependency Inject等的實現。

準備工作

打開網站 https://plnkr.co/edit,點擊“New”->"Angular 2.x",新建一個基本的Angular 2.0腳手架項目。
腳手架項目包含6個文件,分別是 config.js、index.html、README.md、src/app.ts、src/main.ts、style.css。其中,style.css是一個空文件,可以忽略之;README.md文件,熟悉Github這個全球最大的gay gay land的同學應該知道,這是markdown語法的項目說明文件,目前也可以忽略之。

Plunker編輯器

  1. index.html
<!DOCTYPE html>
<html>
  <head>
    <base href="." />
    <title>angular2 playground</title>
    <link rel="stylesheet" href="style.css" />
    <script src="https://unpkg.com/core-js@2.4.1/client/shim.min.js"></script>
    <script src="https://unpkg.com/zone.js/dist/zone.js"></script>
    <script src="https://unpkg.com/zone.js/dist/long-stack-trace-zone.js"></script>
    <script src="https://unpkg.com/reflect-metadata@0.1.3/Reflect.js"></script>
    <script src="https://unpkg.com/systemjs@0.19.31/dist/system.js"></script>
    <script src="config.js"></script>
    <script>
    System.import('app')
      .catch(console.error.bind(console));
  </script>
  </head>
  <body>
    <my-app>
    loading...
  </my-app>
  </body>
</html>

Index.html文件后面基本不會進行任何修改,有興趣了解其中包含JS文件的含義及作用的,請自行Google,俺就不做大自然的搬運工了。
按照慣例,先給index.html,<title></title>標簽后添加Bootstrap 資源鏈接。

<title>angular2 playground</title>
 ......
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link  rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.1.1.min.js" integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8="  crossorigin="anonymous"></script>
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
 ......

index.html文件中有一段這樣的代碼

<my-app>
    loading...
</my-app>

<my-app>標簽是非HTML標準標簽,這是一個Angular Component (組件),需要有一個文件實現對應的my-app組件。
在index.html文件中還引用了config.js文件。

  1. config.js
System.config({
  //use typescript for compilation
  transpiler: 'typescript',
  //typescript compiler options
  typescriptOptions: {
    emitDecoratorMetadata: true
  },
  paths: {
    'npm:': 'https://unpkg.com/'
  },
  //map tells the System loader where to look for things
  map: {
    
    'app': './src',
    
    '@angular/core': 'npm:@angular/core/bundles/core.umd.js',
    '@angular/common': 'npm:@angular/common/bundles/common.umd.js',
    '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
    '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
    '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
    '@angular/http': 'npm:@angular/http/bundles/http.umd.js',
    '@angular/router': 'npm:@angular/router/bundles/router.umd.js',
    '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
    
    '@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js',
    '@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js',
    '@angular/compiler/testing': 'npm:@angular/compiler/bundles/compiler-testing.umd.js',
    '@angular/platform-browser/testing': 'npm:@angular/platform-browser/bundles/platform-browser-testing.umd.js',
    '@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js',
    '@angular/http/testing': 'npm:@angular/http/bundles/http-testing.umd.js',
    '@angular/router/testing': 'npm:@angular/router/bundles/router-testing.umd.js',
    
    'rxjs': 'npm:rxjs',
    'typescript': 'npm:typescript@2.0.2/lib/typescript.js'
  },
  //packages defines our app package
  packages: {
    app: {
      main: './main.ts',
      defaultExtension: 'ts'
    },
    rxjs: {
      defaultExtension: 'js'
    }
  }
});

config.js文件,基本不需要修改,改名控的同學可以修改的位置就兩個地方,分別是'/src'(源代碼路徑)和'./main.ts'(應用程序入口):

'app': './src',
 ......
main: './main.ts',
  1. main.ts 應用程序入口
//main entry point
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {AppModule} from './app';
//從當前文件(main.ts)同文件夾內的app.ts(不需要寫ts后綴)文件中導入AppModule類
platformBrowserDynamic().bootstrapModule(AppModule)
//啟動AppModule
  1. app.ts
//our root app component
import {Component, NgModule} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'
//組件模板
@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2>Hello {{name}}</h2>
    </div>
  `,
})
//組件類
export class App {
     name:string;
     constructor() {
       this.name = 'Angular2'
     }
}
// 啟動模塊 AppModule
@NgModule({
  imports: [ BrowserModule ],
  declarations: [ App ],
  bootstrap: [ App ]
})
export class AppModule {}

Template 中使用 ` 而不是單引號 ' ,原因請看 模板字符串 說明。
因為代碼不多,Module、Component、Template三個部分寫在同一個ts文件中。

我們的目標

  • 美觀大方的界面
  • 實現SPA頁面路由
  • 實現組件Javascript、HTML代碼文件分離
界面布局實現

把app.ts拆分為app.ts、app.component.ts、app.template.html

  1. app.ts
 //our root app component
 import {NgModule} from '@angular/core'
 import {BrowserModule} from '@angular/platform-browser'

 import {App} from './app.component' 

 @NgModule({
      imports: [ BrowserModule ],
      declarations: [ App ],
      bootstrap: [ App ]
})
export class AppModule {}
  1. app.component.ts**
import {Component} from '@angular/core'
@Component({
  selector: 'my-app',
  moduleId: __moduleName,
  templateUrl: './app.template.html'
})
export class App {
  name:string;
  constructor() {
    this.name = 'Angular2'
  }
}
  1. app.template.html
<nav class="navbar navbar-inverse navbar-fixed-top">
  <div class="container-fluid">
    <div class="navbar-header">
      <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#topNavbar">
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="#">AngularJS Demo</a>
    </div>
    <div class="collapse navbar-collapse" id="topNavbar">
      <ul class="nav navbar-nav">
        <li class="active"><a href="#">首頁</a></li>
        <li><a href="#">Page 1</a></li>
      </ul>
      <ul class="nav navbar-nav navbar-right">
        <li><a href="#"><span class="glyphicon glyphicon-user"></span> Sign Up</a></li>
        <li><a href="#"><span class="glyphicon glyphicon-log-in"></span> Login</a></li>
      </ul>
    </div>
  </div>
</nav>
<nav class="navbar navbar-default navbar-fixed-bottom">
  <div class="container-fluid">
     <div class="row">
            <div class="col-md-12 navbar-text">? 2017 | <a href="http://www.lxweimin.com/u/15de06e8d059" target="_blank" class="navbar-link">程序員長春</a>
            </div>
        </div>
  </div>
</nav>
組件代碼分離

為簡單起見,只實現兩個路由頁面,home和login。

  1. 添加文件 src/home/home.component.ts、src/home/home.template.html
    ** home.component.ts **
 import {Component} from '@angular/core'

 @Component({
  selector: 'home',
  moduleId: __moduleName,
  templateUrl: './home.template.html'
})
export class HomeComponent {
}

** home.template.html **

// 這部分HTML只是為了頁面好看,隨便寫了點,如果沒有,也是可以的。
<div class="jumbotron">
  <div class="container">
    <div class="page-header text-center">
      <h1>AngularJS Demo</h1>
    </div>
    <p>AngularJS 演示程序,包括且不限于Module, Route, LocalStorageService, ajax web API consumer等功能。</p>
  </div>
</div>
  1. 添加文件 src/login/login.component.ts、src/login/login.template.html
    ** login.component.ts **
 import {Component} from '@angular/core'

 @Component({
  selector: 'login',
  moduleId: __moduleName,
  templateUrl: './login.template.html'
})
export class LoginComponent{
}

** login.template.html **

// 這部分HTML只是為了頁面好看,隨便寫了點,如果沒有,也是可以的。
<div class="container">
  <div id="loginbox" style="margin-top:100px;" class="mainbox col-md-6 col-md-offset-3 col-sm-8 col-sm-offset-2">
    <div class="panel panel-info">
      <div class="panel-heading">
        <div class="panel-title">Sign In</div>
      </div>
      <div style="padding-top:30px" class="panel-body">
        <div style="display:none" id="login-alert" class="alert alert-danger col-sm-12"></div>
        <form id="loginform" class="form-horizontal" role="form">
          <div style="margin-bottom: 25px" class="input-group">
            <span class="input-group-addon"><i class="glyphicon glyphicon-user"></i></span>
            <input id="login-username" type="text" class="form-control" name="username" value="" placeholder="username or email">
          </div>
          <div style="margin-bottom: 25px" class="input-group">
            <span class="input-group-addon"><i class="glyphicon glyphicon-lock"></i></span>
            <input id="login-password" type="password" class="form-control" name="password" placeholder="password">
          </div>
          <div style="margin-top:10px" class="form-group">
            <!-- Button -->
            <div class="col-sm-12 controls">
              <a id="btn-login" href="#" class="btn btn-success">Login  </a>
            </div>
          </div>
        </form>
      </div>
    </div>
  </div>
</div>
頁面路由實現
  1. 修改app.ts, app.template.html
    ** app.ts **
 //our root app component
 import {NgModule} from '@angular/core'
 import {BrowserModule} from '@angular/platform-browser'
import {App} from './app.component'
--- 添加路由代碼 ---
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent }   from './home/home.component';
import { LoginComponent }     from './login/login.component';
const appRoutes: Routes = [
  { path: 'home', component: HomeComponent },
  { path: 'login', component: LoginComponent },
  { path: '',   redirectTo: 'home', pathMatch: 'full' }
];
@NgModule({
  imports: [ BrowserModule, RouterModule.forRoot(appRoutes) ],
  declarations: [ App, HomeComponent, LoginComponent ],
  bootstrap: [ App ]
})
--- 修改結束 ---
export class AppModule {}

** app.template.html **
修改超級鏈接為路由鏈接,加入路由選擇器"<router-outlet></router-outlet>"。

--- 新代碼 ---
<a class="navbar-brand" routerLink="/home">{{ name }} Demo</a>
... ... 
<li><a routerLink="/login"><span class="glyphicon glyphicon-log-in"></span> Login</a></li>
......
<router-outlet></router-outlet>

總結

在本文中,我們從一個最基本的Angular腳手架,一步步添加內容,實現了Bootstrap布局,Angular 路由,一個簡單的首頁和一個登陸頁面。

首頁

登陸

在線預覽請點擊: Angular 2.0 Demo

下篇預告

添加一個受保護頁面,只有通過驗證后才能訪問收保護頁面。

系列文章目錄

  1. Angular 2.0 SPA應用 - 從腳手架開始 (1)
  2. Angular 2.0 SPA應用 - 身份認證(2)
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,702評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,143評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,553評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,620評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,416評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,940評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,024評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,170評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,709評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,597評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,291評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,029評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,407評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,663評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,403評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,746評論 2 370

推薦閱讀更多精彩內容