第三章 Angular Route導航

一、SPA的概念

首先明確一個概念,SPA,全稱為Single Page Application單頁應用,一個單頁應用是一個旨在只加載一次,不再刷新,只改變頁面上部分內容的一個應用。在單頁應用中,瀏覽器不會跳轉,只會停留在當前頁面。

Angular應用就是SPA,它使用路由器來實現根據用戶的操作改變頁面的內容而不重新加載頁面。

每個應用都有一個路由器,你需要配置這個路由器使其滿足你的需求,路由器的另一個作用就是為每一個視圖分配一個唯一的URL,這樣你就可以用這個URL使某個應用跳到特定的視圖狀態。

按照Angular的規定,在一個插座上,只能展示一個組件,而我們在上一章中并沒有把輪播圖組件和商品列表組件封裝在一起,所以本章會對上一章的component進行一個更改。如果上一章的內容你還沒有學習,那你可以點擊這里學習上一章的內容

二、路由的基礎知識

1、路由相關的對象

在Angular里面主要提供下面五個對象來處理路由相關的功能:

  • Routes 路由配置,保存著哪個URL對應展示哪個組件,以及在哪個RouterOutlet中展示。

  • RouterOutlet 在Html中標記路由容易呈現位置的占位符指令。

  • Router 負責運行時執行路由的對象,可以通過調用其navigate()和navigateByUrl()方法來導航到一個指定的路由。

  • RouterLink 在Html中聲明路由導航用的指令。

  • ActivetedRoute 當前激活的路由對象,保存著當前路由的信息,如路由地址、路由參數等。

    注意區別Router和RouterLink
    Router和RouterLink的作用是一樣的,都是用來讓你的應用導航到指定的路由,不同的地方是,Router實在控制器里用的,就是通過編程調用其方法來實現導航,然后RouterLink實在Html模板中用的,它是在<a></a>標簽上使用,通過點擊<a>標簽來導航到指定的路由。

2、Angular路由對象的位置及屬性

Angular路由對象的屬性

Angular應用是由一些組件組成的,每一個組件都有自己的模板和控制器,應用啟動的時候首先展示AppComponent里面的模板,所有的組件都會封裝在一個模塊里,而路由的配置,也就是Routers對象就是存在模塊中的,Routes對象由一組配置信息組成,每個配置信息都至少包含兩個屬性,pathcomponent,path屬性用來指定瀏覽器的Url,component用來指向相應的組件。

由于AppComponent這個組件里面可能包含很多內容,比如多個div,那么,path屬性為/user時,我們的這個A組件應該展示在AppComponent組件模板的什么位置呢?這就需要在AppComponent組件模板中使用RouterOutlet指令來指定組件A的位置。如果想要顯示B組件的話,可以在頁面上設置一個連接來改變瀏覽器的地址,而RouterLink就是用來在模板上生成這樣一個連接,另外我們也可以通過在組件的控制器中調用Router對象的navigator()方法來改變瀏覽器的地址,從而實現路由的轉換,最后我們可以在路由時通過Url來傳遞一些數據,比如User?name='參數',而這些數據就會保存在ActivetedRoute中。

3、根據實際項目講解路由的各個對象的使用

Routes

我們先新建一個項目:

ng new router --routing

新建完成之后,和上一章項目,app這個目錄下面,當我們使用routing參數新建一個項目的時候,會多生成一個app-routing.module.ts文件,這就是當前應用的一個路由配置。

接下來生成兩個組件,一個是home,一個是product,這里我希望在點擊home的連接的時候顯示home組件的內容,點擊product的連接的時候顯示product的內容:

ng g component home
ng g component product

我們先改一下兩個組件的模板內容,你也可以改成任意你想改的內容。

app-routing.module.ts的路由配置,添加如下代碼:

import {HomeComponent} from './home/home.component';

const routes: Routes = [
    {path: '', component: HomeComponent},   //當路徑為空的時候,展示HomeComponent
    {path: 'product', component: ProductComponent},   //當路徑為product的時候,展示ProductComponent
];

這里的Routes就是前面提到的路由的第一個對象。

需要注意的是,在Angular的路由配置中,配置路徑時path這個變量不能使用/開頭,就是不能把上面的寫成/product,這樣是不對的!!!因為Angular的路由器會為我們解析和生成Url,不用/開頭是為了在各個組件間自由使用相對路徑和絕對路徑。

RouterOutlet

RouterOutlet就是我們前面提到的插座。現在回過頭去看App.component.html,你會發現<router-outlet></router-outlet>已經自動生成了,他的作用就是指示當前導航到某一個路由的時候,對應要顯示的組件要顯示在哪兒,顯示在<router-outlet></router-outlet>這個插座的后面。在后面的內容會看到最后生成的頁面的樣子。

RouterLink

app.component.html做如下更改

<a [routerLink]="['/']">主頁</a>
<a [routerLink]="['/product']">商品詳情</a>

<router-outlet></router-outlet>

需要注意的是,這里寫路徑的時候,必須要用一個斜杠開頭,因為后面還有子路由,也就是說在這里用不同的斜杠加上點來區分是導航到一個子路由還是導航到根路由

當寫的是<a [routerLink]="['/product']">商品詳情</a>這樣的時候,導航的是根路由,而app-routing.module.ts里面做配置的路由全都是根路由。

運行項目,看一下頁面的源碼實際是下面這個樣子:


商品詳情

到這里,相信善于思考的你肯定產生了這樣的疑問,為什么routerLink的參數是一個數組而不是我們平時常見的字符串形式?原因很簡單,因為當我們需要在路由時傳遞一些參數,我們可以在數組后面添加要傳遞的參數,參數到底如何傳遞,又是如何接收的,后面的章節會詳細的介紹。

重要的事情再說一遍,使用routerLink時,它的參數是一個數組!

Router

更改app.component.html,添加一個input。

<a [routerLink]="['/']">主頁</a>
<a [routerLink]="['/product']">商品詳情</a> 
//新加Input
<input type="button" value="商品詳情" (click)="toProductDetails()" >

<router-outlet></router-outlet>

這里我們看到一個新的語法,就是用一個小括號括起來的click,這個就是Angular的數據綁定的第三種方式,叫做事件綁定。意思就是我新加的input的點擊事件是通過控制的toProductDetails()方法來實現的。

現在就在app.component.ts里面去寫toProductDetails()這個方法:

import { Component } from '@angular/core';             
import {Router} from '@angular/router';                
                                                       
@Component({                                           
    selector: 'app-root',                              
    templateUrl: './app.component.html',               
    styleUrls: ['./app.component.css']                 
})                                                     
export class AppComponent {                            
    title = 'app';                                     
                                                       
    constructor(private router: Router){               
                                                       
    }                                                  
    //下面這個方法是從它的構造函數來的                                                  
    toProductDetails(){                                
        this.router.navigate(['/product']);            
    }                                                  
}                                                      

頁面上的routerLink傳什么值,后臺navigate就傳什么值,這樣出來的效果是一樣的。只不過一個是通過頁面的連接跳轉的,另一個是通過控制器的方法跳轉。

我們在引用一些包的時候需要添加引用,對于webstorm開發工具的各個快捷鍵,你可以參考webstorm快捷鍵大全

4、解決錯誤連接的問題

當我們輸入不存在的連接的時候,服務器會報Error: Cannot match any routes.的錯誤,這里我們使用一個通配符來解決錯誤連接的問題。我們先用以下命令再生成一個code404組件:

ng g component code404

當我們在頁面上輸入一個不存在的路由地址的時候,顯示這個code404組件的內容。

頁面很簡單,直接寫為:

<p>
  頁面不存在
</p>

這里新加了組件,記得在app-routing.module.ts里面添加對應的路由

{path: '**', component: Code404Component},

路由匹配是基于優先匹配的原則,所以通配符的路由要放在最后。

5、如何在路由時傳遞數據

路由時傳遞數據的方式主要有三種

  • 第一種,在查詢參數中傳遞數據
/product?id=1&name=2     ==>  ActivetedRoute.queryParams[id]

使用這種方式傳遞數據后,在路由的目標組件中,可以通過ActivetedRoute.queryParams[id]這個參數來獲取傳遞的數據。

實例:app.component.html修改代碼如下:

<a [routerLink]="['/']">主頁</a>
<a [routerLink]="['/product']" [queryParams]="{id: 1}">商品詳情</a>       //新增了參數

<input type="button" value="商品詳情" (click)="toProductDetails()">

<router-outlet></router-outlet>

點擊商品詳情連接后,瀏覽器的地址是這樣的:http://localhost:4200/product?id=1

那么如何在商品詳情組件獲取這個參數呢?這就需要用到路由對象的最后一個對象ActivetedRoute

product.component.ts里添加如下代碼:

export class ProductComponent implements OnInit {

    private productId: number;

    constructor(private routeInfo: ActivatedRoute) { }

    ngOnInit() {
        this.productId = this.routeInfo.snapshot.queryParams['id'];   //snapshot接下來會講解
    }
}

然后把product的屬性通過插值表達式顯示出來:
product.component.html

<p>
    商品ID是:{{productId}}
</p>
  • 第二種,在路由路徑中傳遞數
{path:/product/:id}     ==> /product/1   ==>  ActivitedRoute.params[id]

使用這種查詢方式,在定義路由的路徑時就要先指定參數的名稱,然后在實際的路徑中攜帶這個參數。最后,在路由的目標組件中,可以通過ActivitedRoute.params[id]這個參數來獲取傳遞的數據。

在路由路徑中傳遞數需要三步:
1、修改路由配置中的path屬性,使其可以攜帶參數。
以app-routing.module.ts為例:

{path: 'product/:id', component: ProductComponent},          //添加  /:id

2、修改路由連接的參數來傳遞參數。
以app.component.html為例:

<a [routerLink]="['/product', 1]">商品詳情</a>

3、修改獲取參數的方式,讓它從Url中獲取:
只需要把product.component.ts里面的queryParams改為params就可以,像這樣:

this.productId = this.routeInfo.snapshot.params['id'];
  • 第三種、在路由配置中傳遞數據
{path:/product, component: ProductComponent, data:[{isProd: true}]}    

==>   ActivitedRoute.data[0][isProd]

使用這種方式傳遞數據后,在路由的目標組件中,可以通過ActivitedRoute.data[0][isProd]這個方式來獲取傳遞的數據。

6、參數快照 和 參數訂閱

snapshot 參數快照
subscribe 參數訂閱

先看看product.componet.ts的代碼

export class ProductComponent implements OnInit {

    private productId: number;

    constructor(private routeInfo: ActivatedRoute) { }

    ngOnInit() {
          this.productId = this.routeInfo.snapshot.params['id'];    //參數快照
    }
}

當我們切換點擊兩個商品詳情時,會出現下圖這個問題,瀏覽器的URL改變了,但是商品ID并沒有改變。而如果點擊主頁再點擊商品詳情,商品ID會改變。這就是商品快照所帶來的問題。那么怎么解決呢?

參數快照

每次從home組件路由到商品詳情組件的時候,商品詳情會被創建,在商品詳情這個組件被創建的時候,它的constructor方法會被調用,它的ngOnInit()方法會被調用一次,但是當我們從商品詳情組件路由到商品詳情組件,也就是自身路由到自身的時候,由于商品詳情組件已經被創建了,所以它不會再次被創建,也就是說它的ngOnInit()方法就不會被調用,所以productId屬性依然保持著第一次被創建時所賦予的值。所以解決的辦法為,把初始化改為商品訂閱的方式:

  this.routeInfo.params.subscribe((params: Params) => this.productId = params['id']);   //參數訂閱

更改為商品訂閱以后,可以正常的顯示,但是并沒有嗎,每次都創建商品詳情組件,但是我們訂閱了商品IDproductId,所以每次都會獲取到改變后的商品IDproductId的值,然后拿改變的值重新修改本地的productId

7、重定向路由

在用戶訪問一個特定的地址時,將其重定向到另一個指定的地址。
例如:

www.aaa.com   ==>   www.aaa.com/products

www.aaa.com/x   ==>   www.aaa.com.y

在app-routing.module.ts做更改:

const routes: Routes = [
    {path: '', redirectTo: '/home', pathMatch: 'full'},   //重定向路由
    {path: 'home', component: HomeComponent},
    {path: 'product/:id', component: ProductComponent},
    {path: '**', component: Code404Component},
];

對應的app.component.html中的第一行代碼改成:

<a [routerLink]="['/home']">主頁</a>

之前定義的主頁的組件對應的路由是一個空字符串,這樣是不太好的習慣,更好的是路徑的名字和組件的名字是一致的,這樣的話無論是開發還是修改的時候都能很好的理解當前的配置。

8、子路由

相當于一個占位符,在Angular中根據路由狀態動態插入視圖。子路由會形成一個父子關系。

子路由的語法非常簡單,我們先看一個普通的路由:

{path:'home', component: HomeComponent}

當我想配置子路由的時候,只需要在組件的路由上加一個children屬性,這個children屬性是一個數組,數組里面可以再去配置標準的路由,像下面這樣:

{path:'home', component: HomeComponent
     children:[
     {
       path: '',component: XxxComponent,
    },
    {
       path: '/yyy',component: YyyComponent
    }
    ]
}

上面這兩個子路由在訪問的home的時候都會展示HomeComponent組件的內容,同時這個組件模板上RouterOutlet的位置會展示XxxComponent組件的模板。

在訪問的home/yyy的時候都會展示HomeComponent組件的內容,同時這個組件模板上RouterOutlet的位置會展示YyyComponent組件的模板。

現在來一步一步的實現子路由

先新建兩個組件來顯示商品的描述信息和銷售員的信息

ng g component productDesc        //商品描述信息組件
ng g component sellerInfor     //銷售員信息

修改seller-infor.component.html頁面讓它顯示銷售員的ID:

<p>
  銷售員ID是:{{sellerId}}
</p>

然后在銷售員信息的控制器seller-infor.component.ts里面去寫一個sellerId

export class SellerInforComponent implements OnInit {

    private sellerId:number;

    constructor(private routeInfor: ActivatedRoute) { }   //在構造函數里面引入ActivatedRoute

    ngOnInit() {
        this.sellerId = this.routeInfor.snapshot.params['id'];   //這里的ID會通過URL的方式傳進來。
    }
}

兩個組件修改完了, 下一步需要修改一下路由配置來給商品組件加上子路由,因為新建的兩個組件productDesc和sellerInfor都是在商品詳情里面顯示,所以這兩個組件的路由需要加在商品詳情的組件路由的下面。

app-routing.module.ts控制器下添加子路由

const routes: Routes = [
    {path: '', redirectTo: '/home', pathMatch: 'full'},
    {path: 'home', component: HomeComponent},
    {path: 'product/:id', component: ProductComponent,   //商品詳情組件路由
        children:[       //添加子路由,每一個配置其實和路由的配置是一樣的
            {path: '', component: ProductDescComponent},
            {path: 'seller/:id', component: SellerInforComponent},
        ]},
    {path: '**', component: Code404Component},
];

這就是組路由的配置,子路由的配置是相對于主路由的配置來的。 如果想要顯示SellerInforComponent組件的信息,在瀏覽器的地址欄就要輸入product/:idseller/:id的組合,像這樣:http://localhost:4200/product/1/seller/99

最后一步修改product.component.html頁面:

<p>
    這里是商品列表組件。
</p>
<p>
    商品ID是:{{productId}}
</p>
//  同樣添加兩個鏈接在這里 
<a [routerLink]="['./']">商品描述</a>    
<a [routerLink]="['./seller', 99]">銷售員信息</a>    //通過Url傳遞參數ID
<router-outlet></router-outlet>   //添加一個插座,用來顯示商品的描述信息和銷售員的信息

注意上面這兩個鏈接的routerLink不能寫斜杠/,因為斜杠是根路由,而這里是希望在當前路由下找到它的子路由,那么就需要寫成./也就是 點 加 斜杠。

./的意思是當前這個鏈接要指向當前這個Html這個控件的路由的子路由,子路由的字符串是一個空字符串。

插座是可以無限嵌套的,最終這些插座會形成一個父子關系這是第一點,第二點要注意的是,路由信息是在模塊層的,組件本身并不知道任何路由相關的信息。

9、輔助路由

輔助路由允許你定義多個插座,并可以同時控制每一個插座需要顯示的內容。

聲明一個輔助路由需要三步

  • 第一步:在組件的模板上面,聲明兩個插座,其中一個帶有name屬性:
<router-outlet></router-outlet>
<router-outlet name="aux"></router-outlet>   //name名稱任意
  • 第二步:配置路由。在路由配置里面配置名字叫aux的插座上可以顯示哪些組件。
{path: 'xxx', component: XxxComponent, outlet: 'aux'}
{path: 'yyy', component: YyyComponent, outlet: 'aux'}
  • 第三步:設置導航。導航的時候你需要去指定在路由到某一個地址的時候,在輔助的路由上面要顯示哪個組件。比如下面這個例子,當我點擊Xxx連接的時候,我的主插座會導航到home這個插座上,顯示home這個組件,而輔助的插座上,aux這個插座會顯示xxx這個組件。點擊Yyy連接也是同樣的道理。
<a [routerLink]= "['/home', {outlets: {aux: 'xxx'}}]">Xxx</a>
<a [routerLink]= "['/product', {outlets: {aux: 'yyy'}}]">Yyy</a>

接下來接著前面的內容來實現一個在瀏覽商品是可以隨時找客服聊天的這樣一個功能從而展現輔助路由的一個用法。先梳理一下大概的思路:

輔助路由案例整體思路

  • 在app組件的模板上在定義一個插座來顯示聊天面板。
  • 單獨開發一個聊天室組件,只顯示在新定義的插座上。
  • 通過路由參數控制新插座是否顯示聊天面板。

首先在app模板上再定義一個插座來顯示聊天面板在app.component.html的主路由后面加上,再添加兩個鏈接:

<a [routerLink]="[{outlets: {aux: 'chat'}}]">開始聊天</a>
<a [routerLink]="[{outlets: {aux: null}}]">結束聊天</a>

<router-outlet name="aux"></router-outlet>    //輔助路由

然后單獨開發一個聊天組件,用ng g component chat命令生成一個聊天組件并修改一下他的模板的內容,因為要輸入內容,所以改為文本域的形式:

<textarea class="chat" placeholder="請輸入聊天組件"></textarea>

在樣式表里面設置一下文本域的樣式:

.chat{
    background: aquamarine;
    height: 100px;
    width: 30%;
    float: left;
    box-sizing: border-box;
}

為了讓商品和聊天室并排,我們需要改一下下面組件的樣式內容:
home.component.html

<div class="home">    //新加div
    <p>
        這里是主頁組件。
    </p>
</div>

home.component.css

.home{
    background: red;
    height: 100px;
    width: 70%;
    float: left;
    box-sizing: border-box;
}

product.component.html

<div class="product">
    <p>
        這里是商品列表組件。
    </p>
    <p>
        商品ID是:{{productId}}
    </p>

    <a [routerLink]="['./']">商品描述</a>
    <a [routerLink]="['./seller', 99]">銷售員信息</a>
    <router-outlet></router-outlet>
</div>

product.component.css

.product{
    background: deepskyblue;
    height: 100px;
    width: 70%;
    float: left;
    box-si zing: border-box;
}

好了,三個組件都改造完了,主頁組件和商品組件占頁面的70%,聊天室占剩下的30%。

最后在app.component.ts里面添加路由配置:

{path: 'chat', component: ChatComponent, outlet: 'aux'},

好了,現在可以看到頁面應該是這個樣子的。


頁面
10、路由守衛

在講解路由守衛之前,我們先來考慮一些特殊的場景,在這些場景中,只有用戶滿足某些條件以后才會被允許獲進入或離開一個路由,比如:

  • 只有當用戶已經登錄并擁有某些權限時才能進入某些路由。
  • 一個由多個表單組件組成的向導,例如注冊流程,用戶只有在當前路由組件中填寫滿足要求的信息才可以導航到下一個路由。
  • 當用戶未執行保存操作而試圖離開當前導航時提醒用戶。

angular的路由系統提供了一些鉤子來幫助你進入和離開路由,可以使用這些鉤子來實現前面的這些場景,我們稱這些鉤子為路由護衛,也稱為路由守衛。

接下來介紹三種路由守衛:

  • CanActivate:處理導航到某路由的情況。當你不能滿足某一要求時,你就不能到達某一路由。
  • CanDeactive:處理從當前路由離開的情況。如果不能滿足要求,就不能離開當前路由。
  • Resolve:在路由激活之前獲取路由數據。

在之前的例子中,做一個用戶必須登錄才能看到商品信息,不過為了節省時間,這里的登錄就做一個假登錄,用一個隨機數來進行判斷,就不實現真正的登錄了。

先在APP目錄下添加一個guard文件,再guard文件中新建一個login.gaurd.ts,編寫代碼如下:

import {CanActivate} from '@angular/router';

export class LoginGuard implements CanActivate{

    canActivate(){
        const loggedIn: boolean = Math.random() < 0.5;   //假設隨機數小于0.5就是登錄

        if (!loggedIn){
            console.log('用戶未登錄!');
        }
        return loggedIn;
    }
}

再添加一個是否保存的彈窗示例:
unsave.guard.ts文件

import {ProductComponent} from '../product/product.component';
import {CanDeactivate} from '@angular/router';

export class UnsavedGuard implements CanDeactivate<ProductComponent>{
    canDeactivate(component: ProductComponent){
        return window.confirm('你還沒有保存,確定要離開嗎?');
    }
}

然后修改配置,把路由加在產品信息上。

{path: 'product/:id', component: ProductComponent, children:[
        {path: '', component: ProductDescComponent},
        {path: 'seller/:id', component: SellerInforComponent},
    ], canActivate: [LoginGuard]},
      canDeactivate: [UnsavedGuard]

@NgModule({
    providers: [LoginGuard, UnsavedGuard]      //添加此行
})

canActivate的值也是接收一個數組,可以接收多個路由守衛,當應用視圖進入此路由時,這個數組里的所有守衛會被依次調用,如果其中一個守衛返回fasle,則路由請求會被拒絕。

11、resolve守衛

resolve守衛的作用是在進入主頁之前,把主頁需要的數據都加載好,如果拿不到想要的數據或拿數據的時候出問題了就直接跳到錯誤信息頁或彈出一些提示,就不再進入目標的路由了,

product.resolve.ts

import {ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot} from '@angular/router';
import {Product} from '../product/product.component';
import {Observable} from 'rxjs/Observable';
import {Injectable} from '@angular/core';

@Injectable()
export class ProductResolve implements Resolve<Product>{

    constructor(private router: Router){     //構造函數

    }

    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Product> | Promise<Product> | Product {
        const productId: number = route.params['id'];

        if(productId == 1){     //沒有調取實際的數據,而是設置他等于1時,默認為ID正確
            return new Product(1, 'iPhone7');
        }else{
            this.router.navigate(['/home']);
            return undefined;
        }
    }

}

product.componnet.ts里面聲明Product:

ngOnInit() {
//添加如下信息
        this.routeInfo.data.subscribe((data: {product: Product}) => {
            this.productId = data.product.id;
            this.productName = data.product.name;
        });
    }

export class Product {
    constructor(public id: number, public name: string){
    }
}

修改app-routing.module.ts

{path: 'product/:id', component: ProductComponent, children:[
       {path: '', component: ProductDescComponent},
       {path: 'seller/:id', component: SellerInforComponent},
   ], resolve:{
       product: ProductResolve    //新加
       }
   },


@NgModule({
   providers: [LoginGuard, UnsavedGuard, ProductResolve]  //新加ProductResolve
})

product.component.ts

<p>
       商品名稱是:{{productName}}
</p>

ok明天修改在線競拍網站的相關路由!

12、改造在線競拍網站

在上一節Angular環境搭建與組件開發里,我們搭建了一個簡單的在線競拍網站,接下來利用路由再在這個在線競拍的基礎上做一些改進,當我們點擊某個商品的時候,輪播組件和商品列表組件講替換為商品詳情。

因為下一章講解一來注入的時候還會更改這個地方,所以這一節在商品詳情頁面是顯示商品信息和固定的圖片。

路由實現思路:

  • 1、創建商品詳情組件,顯示商品的圖片和標題。
ng g component productDetail

編輯product-detail.component.ts里的商品詳情的控制器信息

// 1.1編輯商品詳情的控制器信息
export class ProductDetailComponent implements OnInit {

    productTitle: string;     //因為圖片是定好的,只需要傳遞商品的名稱就可以了

    //要接收路由時傳進來的參數,所以在構造函數里注入ActivatedRoute參數
    //ActivatedRoute是保存當前路由信息,比如參數等。
    constructor(private routeInfo: ActivatedRoute) { }


    ngOnInit() {
        //商品快照,這里不會從自身路由到自身,所以直接用快照的方式
        this.productTitle = this.routeInfo.snapshot.params['prodTitle'];
    }

}

更改product.detail.component.html

<!--1.2更改頁面,顯示商品的圖片和標題。-->
<div>
    <img src="http://placehold.it/820x230">     //這是圖片占位符,820x230是圖片尺寸
    <h4>{{productTitle}}</h4>
</div>
  • 2、重構代碼,把輪播組件和商品列表組件封裝進新的Home組件

創建Home組件,更改home.component.html內容,把app.component.html的輪播組件和商品列表組件拷貝到這個組件內:

<!--2.1把輪播組件和商品列表組件封裝進新的Home組件-->
<div class="row carousel-container">
    <app-carousel></app-carousel>
</div>
<div class="row">
    <app-product></app-product>
</div>

對應樣式拷貝過來app.component.css ==> home.component.css

/*2.2把相應的輪播圖組件的樣式也添加過來*/
.carousel-container{
    margin-bottom: 40px;
}
  • 3、配置路由,在導航到商品詳情組件時傳遞商品的標題參數。
    在app.modul.ts里面添加路由
//3.1增加路由。因為這個項目創建的時候沒有用routing參數,所以這里沒有自動生成route的一個模塊,要手動添加。
const routeConfig: Routes = [
    {path: '', component: HomeComponent},
    {path: 'product/:prodTitle', component: ProductDetailComponent}
]

//3.2聲明完路由配置以后,把路由配置注入到模塊imports里面去,主模塊里用forRoot()注入
imports: [
      BrowserModule,
      RouterModule.forRoot(routeConfig)  //    3.2新加
  ],
  • 4、修改App組件,根據路由顯示Home組件或商品詳情組件。
    更改app.component.html模板
     <div class="col-md-9">
            <!--4.1更改app.component.html模板,改為一個插座就可以了-->
            <router-outlet></router-outlet>
      </div>
  • 5、修改商品列表組件,給商品標題添加帶routerLink指令的連接嗎,導航到商品詳情路由。
    在product.component.html里面添加商品詳情連接
  <div class="caption">
        <h4 class="pull-right">{{product.price}}</h4>
        <!--5增加路由連接-->
        <h4><a [routerLink]="['/product', product.title]">{{product.title}}}</a></h4>
        <p>{{product.desc}}</p>
  </div>

好了,五步改造完成,現在的效果應該是這樣的:


商品詳情

好了,這一章到這里,下一章會講解依賴注入。

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

推薦閱讀更多精彩內容

  • 路由:根據不同地址加載不同組件,實現單頁面應用 # 1 路由基礎知識 ================= 在ang...
    __凌閱讀 693評論 0 0
  • 一.課程簡介 (注意:這里的AngularJS指的是2.0以下的版本) AngularJS的優點: 模板功能強大豐...
    壹點微塵閱讀 940評論 0 0
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,781評論 18 139
  • 第一節:初識Angular-CLI第二節:登錄組件的構建第三節:建立一個待辦事項應用第四節:進化!模塊化你的應用第...
    接灰的電子產品閱讀 13,709評論 64 25
  • 路由是導航的另一個名字。路由器就是從一個視圖導航到另一個視圖的機制。 1.導航,那么我們專門做一個導航組件。這個組...
    價值投機168閱讀 2,110評論 0 2