【Angluar2】TypeScript和Angluar2

前言
1、TypeScript 是一種由微軟開發的自由和開源的編程語言,它是JavaScript的一個超集,擴展了JavaScript的語法,是Angular 官方推薦的前端開發語言。
2、Angular 2 應用可以使用 TypeScript 編寫,也可以使用純 JavaScript 來編寫,但一般會選擇 TypeScript ,主要是因爲: 
              TypeScript 增強了 JavaScript 的類型、類和接口,模仿強類型語言,若你是一個 C# 或者 Java 開發者,你可能會更喜歡使用這種強類型語言來
      開發應用。一些可用於開發TypeScript的IDE(例如Atom),提供了代碼提示和自動補全功能,並提示語法或類型錯誤,可以像服務器端那樣進行客戶
      端開發。
4、Angular2應用由模塊和組件構成,通過ng命令創建工程後,默認生成模塊和組件ts文件,以及測試相關ts文件和組件相關的html模板、樣式等文件。               這幾個文件的關係是從屬關係:
       |--app.module.ts(模塊)
           |--app.component.ts(組件)
              |--app.component.html(HTML模板)
              |--app.component.css(CSS樣式表)
       一個模塊可以有多個組件,一個組件也可以有多個樣式表,但只有一個HTML模板。

-----------------------------------------------------------------------------------------------------------------------------------------------------
Angular2提供了大量內置模塊、組件、服務、指令等。
模塊:
...... 模塊就是將一系列組件、指令、服務等整合封裝到一起,提供一個完整的功能。
...... 模塊在其自身的作用域裏執行,而不是在全局作用域裏,這意味着定義在一個模塊裏的變量,函數,類等等在模塊外部是不可見的,但可使用export導出該模塊,其它模塊
       就可使用import導入該模塊,這樣其它模塊就可使用該模塊導出的類,接口,變量,方法等。      
...... 每個應用會有一個根模塊,按照約定,它的類名叫做AppModule,放在app.module.ts文件中,使用根模塊啓動應用。
       每個根模塊會有一個根組件,默認是app.component.ts,名字是app-root。
       查看項目目錄中的index.html,會發現有<app-root>Loading...</app-root>代碼,就是加載這個根模塊。
...... Angular2核心庫提供了NgModule、FormsModule、HttpModule等若干模塊。
...... 創建模塊 (以app.module.ts爲例,也可以創建自定義模塊)
       import { BrowserModule } from '@angular/platform-browser';    //導入瀏覽器模塊,瀏覽器中運行的應用根模塊都需要導入該模塊  
       import { NgModule } from '@angular/core';                                    //導入核心庫NgModule模塊,每個模塊都需要導入該模塊 
       import { FormsModule } from '@angular/forms';                            //導入表單模塊,在應用中使用表單時需要導入該模塊  
       import { HttpModule } from '@angular/http';                                 //導入http模塊,當需要進行http遠程請求時需要導入該模塊
       import { AppComponent } from './app.component';                      //導入自己創建的組件  *** (導入模塊from後帶有@,導入組件則沒有) ***
       @NgModule({                                                                                     //使用@NgModule()語句描述本模塊的元數據
              declarations: [    //聲明屬於本模塊的組件
                    AppComponent                       
              ],  
              imports: [           //導入本模塊中要用到的模塊                                        
                    BrowserModule,  
                    FormsModule,  
                    HttpModule  
             ],  
             exports: [           //本模塊需要導出的一些模塊、組件、指令等                                                 
                    BrowserModule,  
                    FormsModule,  
                    HttpModule  
             ],  
             providers: [],                               //聲明(注入)模塊中需要使用的服務提供者(類)
             bootstrap: [AppComponent]   //指定根模塊中的引導組件(一般只有一個引導組件),應用啓動過程中,會創建這個數組中的組件並插入到HTML中
     })  
     export class AppModule { }           //導出該(根)模塊,讓其他模塊可用到當前模塊中的類和變量等

組件:
...... 組件定義了一系列方法和屬性,並把HTML模板封裝起來,組件其實就是一個類。
       定義的一段HTML代碼,並取個名字,就可以當作HTML標籤使用了。假如組件的名字是my-app,那麼就可以這樣使用:
       <div>
               <my-app><my-app>
      </div>
...... 每個 Angular 應用都至少有一個根組件,整個應用由若干組件組成,而組件化(Component)則是 Angular 的核心靈魂。
...... 創建組件 (以app.component.ts爲例,也可以創建自定義組件)
       import { Component } from '@angular/core';    //導入核心庫組件模塊,所有組件必須導入該模塊 
       @Component({                                                      //使用@Component()來描述本組件的元數據
              selector: 'app-root',                                         //選擇創建的組件名字
              templateUrl: './app.component.html',           //指定HTML模板,使用相對路徑
              styleUrls: ['./app.component.css']               //HTML模板使用的樣式表,可以有多個
              providers: []                                                    //聲明(注入)組件中需要使用的服務提供者(類)
        })
        export class AppComponent {                          //最後,使用export創建組件類,並暴露出去
             title1 = 'Hello World - 111111111';                //在類中可以創建屬性和方法
             title2 = 'Hello World - 222222222';
       }

服務:
...... 服務定義了能夠被其它組件或指令調用的共享代碼,方便組件之間共享數據和方法,服務其實也是一個類,因此服務代碼和普通的類沒什麼區別。
       但爲了區分出是一個服務類,文件名後綴是*.service.ts,類名格式爲XxxService。
...... 獲取數據的服務往往都是異步的,所以使用Promise去封裝獲取的數據,來模擬異步請求。
          Promise是一個異步容器對象,是異步編程的一種解決方案(回調函數和事件),Promise 提供統一的 API,各種異步操作都可以用同樣的方法進行處理,從它可以獲取
       異步操作的消息。可以通過Promise構造函數生成Promise實例:
             const promise = new Promise(function(resolve, reject) {
                    // ... some code
                    if (/* 異步操作成功 */){
                          resolve(value);
                    } else {
                         reject(error);
                    }
            });
...... 創建自定義服務
       import {User} from "../classes/User";
       import {USERS} from "../mock/user.data";
       export class UserService {
              getUsers(): User[] {
                    return Promise.resolve(USERS);
              }
       }
      getUsers函數返回值是一個數組,數組元素是User類的實例,所以使用了getUsers(): User[],還有因爲我們要異步請求獲取數據,所以使用了Promise。
...... 如何使用自定義服務
       例如在組件內使用服務,需要在文件頂部import指定路徑的服務類,然後在providers元數據類指定服務提供類。
       import {Component, OnInit} from '@angular/core';
       import {User} from "./classes/User";
       import {UserService} from "./services/user.service";
       @Component({
             selector: 'my-app',
             //template: '<h1>My First Angular2 Travel</h1>',
             templateUrl: 'app/templates/main.html',
             providers: [UserService]
       })
       export class AppComponent implements OnInit{
             users: User[];
             constructor(
                     private userService: UserService
             ){}
            getUsersData() {
                    this.userService.getUsers().then(users => this.users = users);
           }
           ngOnInit() {
                   this.getUsersData();
          }
     }

路由:
        路由的作用是建立URL與頁面的對應關係,根據不同的URL路徑匹配出相應的組件並顯示在outlet指定的區域。
例如:
       http://localhost:4200/all-people 對應顯示allPeople頁面
       http://localhost:4200/first-come 對應顯示firstCome頁面
1、新建一個app.toutes.ts根路由配置文件,創建根路由配置(rootRouterConfig),其內容爲:
     // 需要從路由模塊中引入Routes類
     import { Routes } from '@angular/router';
     // 對於需要用url鏈接的組件,需要從文件中導入
     import { AllPeopleComponent } from './all-people/all-people.component';
     import { HomePageComponent } from './home-page/home-page.component';
     //根路由定義爲一個數組,數組對象一般有path(對應url)和component(對應組件)兩個屬性,URL帶上相對路徑,找到後顯示
     //對應的組件
     export const rootRouterConfig: Routes = [
           {
                  path: 'first-come',    //對應 http://localhost:4200/first-come
                  component: HomePageComponent
           },
          {
                 path: 'all-people',    //對應 http://localhost:4200/all-people
                 component: AllPeopleComponent
          },
     ]

2、在根模塊AppModule對應app.module.ts文件中創建根路由模塊(rootRouterModule):
      根路由模塊包含了路由需要使用的服務,它以路由配置爲參數,調用RouterModule.forRoot()方法來創建。
      // 在根模塊中導入路由配置 和 路由模塊(需要使用該模塊的forRoot方法)
      import { rootRouterConfig } from './app.routes';  // 注意沒有.ts
      import { RouterModule } from '@angular/router';
      /*
          調用RouterModule.forRoot(config, 策略)方法創建根路由模塊rootRouterModule
          根路由模塊提供兩種路由策略:PathLocationStrategy(默認策略)和HashLocationStrategy,不指定策略參數時使用默認策略
          PathLocationStrategy路由策略需要一個base路徑,設置base路徑有2種方式,最簡單的是在index.html中head標籤內設置<base>,例子:
                 <head>
                       <base href="/">  //表示設置應用基礎 (根) 路徑,這樣網頁上的所有相對路徑在鏈接時,都會在前面加上基礎路徑指向的地址
                       ...... 
                 </head>
          使用HashLocationStrategy創建根路由模塊,例子:
                 const rootRouterModule: ModuleWithProviders = RouterModule.forRoot(rootRouterConfig, {useHash: true});
      */
      const rootRouterModule: ModuleWithProviders = RouterModule.forRoot(rootRouterConfig);   
      @NgModule({ 
              declarations: [],
              // 導入路由模塊
              imports: [rootRouterModule],
              providers: [],
              bootstrap: [AppComponent]
       })
       export class AppModule { }


3、使用RouterOutlet指令,添加組件存放的區域:
      該指令在頁面預留一塊區域,當url改變時,匹配路由配置中的path,匹配成功後就將與path對應的component加載到這個區域裏內顯示。
      例如:
                <div style="font-size:20px;">
                            <router-outlet name="left"></router-outlet> 
                </div>
                <div style="color:red;">
                            <router-outlet name="right"></router-outlet>
                </div>
       這裏name對應的left和right必須和模塊ts文件裏面的outlet相對應。
       然後在rootRouterConfig根路由配置文件內將對應路徑的組件指定outlet屬性,對應代碼:
      export const rootRouterConfig: Routes = [
            {
                    path: 'first-come',    //對應 http://localhost:4200/first-come
                    component: HomePageComponent,
                    outlet:'left'
            },
            {
                    path: 'all-people',    //對應 http://localhost:4200/all-people
                    component: AllPeopleComponent,
                    outlet:'right'
            },
      ]

       到目前爲止,一個簡單的路由就基本配置完成了,只要在url中輸入正確的path,就能在outlet指定的區域顯示對應的組件。但是,跳轉頁面時不應手動修改url,因此需要通過頁面操作(點擊事件、延遲等方式)改變URL,實現path對應組件顯示,這就是下一節講解的路由跳轉。

4、使用RouterLink指令,實現路由跳轉:
               正如第3節所說,路由跳轉是爲了避免手動輸入改變url,最常見的就是點擊事件改變URL實現路由跳轉,然後在對應區域顯示組件。路由跳轉有
      兩種方式。

      指令跳轉:使用RouterlLink指令,接收連接參數爲數組,當事件觸發時,根據數組內參數去從路由配置查找對應的path和對應的組件並顯示。
      例如:
           <div id="navbar" class="collapse navbar-collapse">
                   <ul class="nav navbar-nav">
                         <li><a [routerLink]="['/all-people']">allPeople</a></li>
                         <li><a [routerLink]="['/first-come']">firstCome</a></li>
                         <li><a [routerLink]="['/last-leave']">lastLeave</a></li>
                  </ul>
           </div>
     RouterLink的強大之處是他可以被應用到任何HTML元素上,使得頁面跳轉不需要依賴超鏈接,例如:
          <a routerLink="/form">表單</a>
          <button [routerLink]="['/form']">表單</button>

     代碼跳轉:
             指令跳轉無法做到動態響應,因此使用諸如延遲跳轉等事件時需要使用代碼跳轉。navigate和navigateByUrl是Router類的方法,用來跳轉路由。
     例如:this.router.navigate(['/exampledetail'],{queryParams:{'name':'yxman'}}); //route是ActivatedRoute的實例,使用需要導入ActivatedRoute
     方法定義: 
             navigate(commands: any[], extras?: NavigationExtras) : Promise`<boolean>`
     接口定義:
             interface NavigationExtras {
                     relativeTo : ActivatedRoute
                     queryParams : Params
                     fragment : string
                      preserveQueryParams : boolean
                      preserveFragment : boolean
                      skipLocationChange : boolean
                      replaceUrl : boolean
            }
     this.router.navigate(['user', 1]);  //以根路由爲起點跳轉
       this.router.navigate(['user', 1],{relativeTo: route}); //相對當前路由跳轉
       this.router.navigate(['user', 1],{ queryParams: { id: 1 } }); //路由中傳參數 /user/1?id=1
       this.router.navigate(['view', 1], { preserveQueryParams: true }); //默認值爲false,設爲true,保留之前路由中的查詢參數/user?id=1 to /view?id=1
       this.router.navigate(['user', 1],{ fragment: 'top' }); //路由中錨點跳轉 /user/1#top
       this.router.navigate(['/view'], { preserveFragment: true });  //默認值爲false,設爲true,保留之前路由中的錨點/user/1#top to /view#top
       this.router.navigate(['/user',1], { skipLocationChange: true });  //默認值爲false,設爲true路由跳轉時瀏覽器中的url會保持不變,但是傳入的參數依然有效
       this.router.navigate(['/user',1], { replaceUrl: true }); //未設置時默認爲true,設置爲false路由不會進行跳轉

5、路由傳遞參數:
      單一參數: <a routerLink=["/exampledetail",id]></a> 或 this.router.navigate(['/exampledetail',id]); 或 this.router.navigateByUrl('/exampledetail/id');
      多個參數: <a routerLink=["/exampledetail",{queryParams:'id':'1','name':'yxman'}]></a>
                             或 this.router.navigate(['/exampledetail'],{queryParams:{'name':'yxman'}});
                             或 this.router.navigateByUrl('/exampledetail',{queryParams:{'name':'yxman'}});

6、路由接收參數:
       ngOnInit(){
           this.data = this.route.snapshot.params['id'];
              this.activeRoute.queryParams.subscribe(params => {
                   this.id = params['id'];
                   this.name = params['name'];
           });
     }

後續研究:子路由

指令:
...... 在Angular2中有三種類型的指令(Directive) 
       組件指令:擁有模板的指令。 
       結構型指令:通過添加和移除DOM元素來改變DOM結構的指令。例如Angluar2默認提供的指令:NgFor, NgIf … 
       屬性型指令:改變元素顯示樣式和行爲的指令。例如Angluar2默認提供的指令:NgStyle …
...... 創建自定義指令(以創建屬性型指令爲例:鼠標在元素上懸停時,改變元素背景色)
       屬性型指令至少需要一個帶有@Directive裝飾器修飾的控制器類,@Directive裝飾器指定了一個選擇器名稱,用於指出與此指令相關聯的屬性名字。
       import { Directive, ElementRef, Renderer } from '@angular/core';    //從Angular Core庫中導入需要使用的模塊
      @Directive({
               //@Directive裝飾器需要一個css選擇器來指定selector的值,以便Angular編譯器從模版中識別出關聯到這個指令的HTML
               selector: '[prefixHightlight]'     
      })
      //在@Directive元數據後面,聲明並導出指令對應的控制器類 HighlightDirective,HighlightDirective類包含了prefixHightlight指令的工作邏輯
      export class HighlightDirective {
            //在 HighlightDirective 類的構造函數中注入 ElementRef 和 Renderer 模塊的實例
            //ElementRef 是一個服務,它賦予了我們直接訪問 DOM 元素的能力。注入 ElementRef 的目的在於讓指令可以引用到真實的 DOM 元素
            //Renderer 可以讓 Hightlight 類裏面的邏輯代碼能夠正確的渲染 DOM 元素的樣式
            constructor(elem: ElementRef, renderer: Renderer) {
                  renderer.setElementStyle(el.nativeElement, 'backgroundColor', 'red');
           }
    }
...... 如何使用自定義指令
       創建一個組件模板文件app.component.html:
               <h1>Angualr2 Directive Study</h1>
               <article prefixHightlight>hover me</article>
      定義一個組件app.component.ts:
               import { Component, OnInit } from '@angualr/core';
               @Component({
                       selector: 'prefix-app',
                       templateUrl: 'app.component.html'
               })
               export class AppComponent implements OnInit {
                     constructor() {}
                     ngOnInit(): void {
                            console.log(`AppComponent has inited : ${ Date.now() }`);
                    }
              }
      聲明一個模塊app.module.ts,並在這個模塊中聲明自己定義的指令,以便Angualr在解析模板時,能正確識別自定義指令:
              import { NgModule } from '@angular/core';
             import { BrowserModule } from '@angular/platform-browser';
             import { AppComponent } from './app.component';
             import { HighlightDirective } from './highlight.directive';
             @NgModule({
                      imports: [ BrowserModule ],
                     declarations: [
                             AppComponent,
                             HighlightDirective
                     ],
                      bootstrap: [ AppComponent ]
             })
             export class AppModule {  }
自定義的屬性型指令的運行原理: 
        Angular在編譯模板時,檢測到DOM元素上正在嘗試綁定某些屬性指令,但Angualr並不能識別這些非內置的屬性指令。 
        Angular就會嘗試在declarations 元數據中查找這個指令屬性,Angular 發現HighlightDirective指令後,接着就會去檢查對應的導入語句,從而找到 highlight.directive.ts 中
        導出的類,進而服務宿主元素對應的行爲能力。

枚舉:  

管道:

類:
     類是一個抽象概念,組件、服務、指令等都是類。

-----------------------------------------------------------------------------------------------------------------------------------------------------
應用啓動流程
...... 加載根模塊。
...... 加載所需的其他模塊。
...... 創建引導組件。
...... 顯示組件內容。

-----------------------------------------------------------------------------------------------------------------------------------------------------
ng2項目的模塊體系

總體思路就是:
> 1.根模塊負責全局的路由。
> 2.核心模塊負責全局服務,也可以定義一些只在根模塊中使用的組件等,並只能由根模塊引入一次,不再導出。
> 3.共享模塊不做服務的提供,而是定義全局共享的組件等,以及幫助子模塊導入系統模塊,讓子模塊只需要導入此共享模塊就夠了。
> 4.子模塊內部可以細分自己的子路由到具體的子組件,以及提供自己的服務等。
> 5.除了頁面入口模塊(即除了根模塊外的具體業務模塊)之外的其他子模塊均考慮寫成惰性加載的模塊,以提升頁面引導的速度減少性能浪費。
> 6.當需要一個比較通用的全局服務時,可以將其加入CoreModule,也可以再創建一個僅被根模塊引入的特性模塊。進一步的,甚至可以將此模塊發佈到npm,這就需要更強的編碼能力和技術積累了。
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章