路由

Posted wpengch1

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了路由相关的知识,希望对你有一定的参考价值。

注:学习使用,禁止转载

在web开发中,路由说的是根据浏览器上的URL的规则,将应用程序分成不同的区域。

比如我们访问一个网站的/路径,我们可能访问的是这个网站的home目录,如果我们访问/about,我们可能访问的是“about page”等等。

为什么要路由

在我们的应用程序中定义路由是有用的,因为我们能:

  1. 将应用程序分离成不同的区域
  2. 在应用程序中维护状态
  3. 根据特定的规则保护应用程序的区域

比如,假设我们编写一个inventory的app,它跟我们在前面描述的有点像。

当我们第一次访问时,我们可能会看到一个可以输入字符的输入框,并且获得一个匹配该字符的产品列表。

之后,我们可能想要去点击一个给定的产品去访问它的详细信息。

因为我们的程序是客户端的,所以技术上不要求我们修改页面的时候改变URL。但是值得思考一下:所有的页面都使用相同的URL后果是什么:

  1. 你不能在刷新页面的时候保存你在app中的位置
  2. 你不能去制定书签页,稍后返回来
  3. 你不能和其他人分享这个页面的URL

或者直接说,就是路由让我们定义一个URL字符串,这个URL字符串制定了我们在app中的哪个部分。

在我们的inventory app中,我们可以为每一个activity指定一系列不同的路由,比如:

首页由http://our-app/.代表,当我们访问这个的时候,我们会被重定向到home页面:http://our-app/home

当访问‘About US’的时候,URL可能会变成”http://our-app/about“,如果我们发送http://our-app/about给其他人,其他人会跟我们一样看到相同的页面。

client-side路由是怎么工作的

以前,我们可能编写过服务器端的路由(虽然没有必要完成这个)。通常服务器端路由的工作是,当HTTP请求进来后,服务器会根据请求进来的URL分配一个不同的Controller。

比如,使用express你可能会写下面这样的代码:

var express = require('express');
var router = express.Router();

// define the about route
router.get('/about', function(req, res) 
   res.send('About us');
   );

或者使用Ruby on Rails你可能会像这样写:

     # routes.rb
get '/about', to: 'pages#about'

# PagesController.rb
class PagesController < ActionController::Base
def about
render
end
end

每个框架各不相同,但是大致情况都是,你有一个服务器,它接收一个请求,然后使用路由控制器根据请求的URL路由到一个特定的Controller,在Controller里面根据路径和参数运行一个特定的行动。

客户端路由概念上是非常类似的。只是实现不同。使用客户端路由,每次URL改变时,我们不必都向服务器发送请求,angular2的app我们称为SPA(单页面应用程序,Single Page Application),因为服务器仅仅给了我们一个简单的页面,渲染不同的页面是javascript做的工作。

所以,在我们的Javascript中,怎么实现路由呢?

开始的时候:使用锚标记(anchor tag)

客户端路由有一个黑客(hack)的方式开始:不是使用页面的形式,而是使用锚标记。

你可能已经知道,锚标记就是让你在页面内直接连接到某一个地方,当你点击的时候,浏览器会滚动到定义锚的地方。

比如,我们在一个html页面定义一个锚标记。

<!-- ... lots of page content here ... -->
<a name="about"><h1>About</h1></a>

然后我们访问http://something/#about,浏览器会直接跳转到定义了about锚标记的H1标签那里。

客户端框架的一个聪明做法是利用锚标记,通过格式化它们作为路径,在我们的app中代表路由。

比如,SPA里面的about路由,它可能会像http://something/#/about,这个被叫着基于hash的路由(hash-based routing)

这个技巧的明晰的是,它看起来像一个正常的URL,因为他使用一个锚标记的开始。

进化:HTML5客户端路由

通过引入HTML5,浏览器获得了创建和更改显示URL的能力,而不需要一个新的请求。

这个是通过history.pushState函数完成的,它向Javascript暴露了浏览器的导航历史。

所以现在,不需要依靠锚标记,而是依靠pushState去执行无需重新加载的历史操作。

:fa-info-circle: 这种route方式在angular1中就已经有了,不过需要使用$locationProvider.html5Mode(true)激活它们。

然而,在angular2中,html是默认的方式,在这章的后面,我们会讲解这样将html模式修改为旧的锚标记的模式。

:fa-info-circle: 当时用HTML模式时,有两个事情需要注意

  1. 不是所有的浏览器都支持HTML5模式,如果你需要兼容旧的浏览器,你需要使用锚标记的方式
  2. 服务器必须支持HTML5的路由。

对于服务器必须支持HTML5路由可能是不明晰的,在这章的后面我们会详细讨论。

编写我们的第一个路由

在angular中,配置路由是通过映射路径到处理它们的组件那里。

让我们创建一个具有多个路由的app,在我们的app中有三个路由:

  1. 一个主页面路由,使用/#/home路径
  2. 一个关于页面,使用/#/about路径
  3. 一个联系我们页面,使用/#/contact路径

并且,当我们访问首页的时候,会重定向到home路径。

angular2的组件路由

注意:
知道编写这版的时候(May 5th, 2016, rc.1),rout怎么发生一些变化,下面标志了/@router-deprecated的代码,这些事来自beta17的。
我们会尽快更新组件路由这章。在那之前,许多概念都是相同的。

在angular2中,配置组件路由有三个主要的部分:

  • RouteConfig注解描述我们的app支持的路由
  • RouterOutlet是一个组件占位符,它扩展每个路由的内容
  • RouterLink使用去链接路由

让我详细看看

RouteConfig

为了定义app的路由,我们使用RouteConfig配置我们的组件。

code/routes/basic/app/ts/app.ts

@RouteConfig([
   path: '/', name: 'root', redirectTo: ['/Home'] ,
   path: '/home', name: 'Home', component: HomeComponent ,
   path: '/about', name: 'About', component: AboutComponent ,
   path: '/contact', name: 'Contact', component: ContactComponent ,
   path: '/contactus', name: 'ContactUs', redirectTo: ['/Contact'] ,
])

关于这个组件注意下面这个事情:

  • path标识了路由的URL
  • name唯一标识当前路由,在RouteLink,redirectTo或者Route API中使用
  • component将当前路由与一个组件绑定
  • redirectTo用于将指定路径重定向到一个存在的路径

总的来说,路由的目标是指定那个组件处理那个路径

Redirections:重定向

当我们在路由定义上使用redirectTo,它告诉路由器,当我们访问这个路径的时候,我们想要重定向到另外一个路径。

在上面的代码中,如果我们访问http://localhost:8080/#/的根路径,会被重定向到默认的路由home。

另外一个联系我们的例子:

 path: '/contactus', name: 'ContactUs', redirectTo: ['/Contact'] ,

在这个例子中,如果我们访问http://localhost:8080/#/contactus,我们会看到浏览器重定向到/contact

RouterOutlet

我们的@View有一个模板,它标识了一些div结构,作为导航的部分。和一个叫着router-outlet的指令

当我们改变路由的时候,我们想要去保持我们的模板,仅仅只修改里面的一部分。

在angular中,为了描述我们想要在哪里绘制路由的内容,我们使用RouterOutlet指令去标识

router-outlet预示路由组件在哪里渲染。

为了使用它,我们需要声明ROUTER_DIRECTIVES作为我们组件的directives,然后在html中增加一个标签

code/routes/basic/app/ts/app.ts

@Component(
  selector: 'router-app',
  directives: [ROUTER_DIRECTIVES],
  template: `
  <div>
    <nav>
      <a>Navigation:</a>
      <ul>
        <li><a [routerLink]="['/Home']">Home</a></li>
        <li><a [routerLink]="['/About']">About</a></li>
        <li><a [routerLink]="['/Contact']">Contact us</a></li>
      </ul>
    </nav>

    <router-outlet></router-outlet>
  </div>
  `
)

如果你仔细看了上面的代码,你会发现在最后添加了一个router-outlet,当我们访问/home的时候,这里将会渲染HomeComponent。其他的组件也是一样。

现在我们知道在哪里渲染模板,那我们怎么告诉angular去导航到特定的路由呢?

我们可能试着直接在html建立链接:

<a href="/#/home">Home</a>

但是,如果我们这样做,当我们点击的时候,会发现页面重新加载了。在SPA中,这个永远不是我们需要的。

为了解决这个问题,angular2提供了一个解决方案,它可以让我们连接到特定的路由,但是让页面不重新加载:RouterLink指令。

code/routes/basic/app/ts/app.ts

<ul>
        <li><a [routerLink]="['/Home']">Home</a></li>
        <li><a [routerLink]="['/About']">About</a></li>
        <li><a [routerLink]="['/Contact']">Contact us</a></li>
      </ul>

我们可以看到,在a标签里面使用了[routerLink]。右边,我们有一个数组,”[‘/Home’]” or “[‘/About’]”,它指定我们点击的时候跳转到指定的路由页面。

它可能有点奇怪,它的值是一个字符数组,这是因为当我们点击我们可以做更多的事情,详细信息见自路由一节。

现在,我们仅仅使用route name。

所有东西放在一起

所以,现在,我们每一个部分都有了,就是将他们组合起来。

第一件事情就是编写index.html。下面是所有的代码:

code/routes/basic/app/index.html

<!doctype html>
<html>
  <head>
    <base href="/">
    <title>ng-book 2: Angular 2 Router</title>

    % for (var css in o.htmlWebpackPlugin.files.css)  %
      <link href="%=o.htmlWebpackPlugin.files.css[css] %" rel="stylesheet">
    %  %
  </head>
  <body>
    <router-app></router-app>
    <script src="/core.js"></script>
    <script src="/vendor.js"></script>
    <script src="/bundle.js"></script>
  </body>
</html>

除了这条线,下面的代码应该很熟悉了

<base href="/">

这条线定义了一个基本的HTML标签,这个是用来告诉浏览器,在哪里查找图像和其他资源。

原来,angular路由器也依靠这个来构建它的路由信息。

比如,如果我们有一个/path路径的路由,并且有一个base定义,href=”/app”,完整的路径会混合起来。

有时,angular程序员不会去访问HTML的头部。比如,在一个大型应用程序中,重用头部和脚部的时候。

幸运的是,对于这个事情,有一个解决方案。当启动应用程序的时候,你可以以编程方式定义基础路径。

bootstrap(RoutesDemoApp, [
 ROUTER_PROVIDERS,
 provide(APP_BASE_HREF, useValue: '/')
 ]);

注入provide(APP_BASE_HREF, useValue: ‘/’),等同于使用base=‘/’。

创建组件

在讲解主页面之前,让我们为每一个路由创建一个组件。

HomeComponent

HomeComponent只有一个H1标签,它显示Welcome。

code/routes/basic/app/ts/components/HomeComponent.ts

/*
 * Angular
 */
import Component from '@angular/core';

@Component(
  selector: 'home',
  template: `<h1>Welcome!</h1>`
)
export class HomeComponent 

AboutComponent

同理,AboutComponent也仅仅只有一个h1

code/routes/basic/app/ts/components/AboutComponent.ts

/*
 * Angular
 */
import Component from '@angular/core';

@Component(
  selector: 'about',
  template: `<h1>About</h1>`
)
export class AboutComponent 

ContactComponent

跟aboutComponent一样。

code/routes/basic/app/ts/components/ContactComponent.ts

/*
 * Angular
 */
import Component from '@angular/core';

@Component(
  selector: 'contact',
  template: `<h1>Contact Us</h1>`
)
export class ContactComponent 

Application component

现在我们创建一个根组件,将这些组件组合起来。

开始导入需要的指令

code/routes/basic/app/ts/app.ts

import provide, Component from '@angular/core';
import bootstrap from '@angular/platform-browser-dynamic';
import 
  ROUTER_DIRECTIVES,
  ROUTER_PROVIDERS,
  RouteConfig,
 from '@angular/router-deprecated';

import LocationStrategy, HashLocationStrategy from '@angular/common';

接下来,导入我们上面创建的组件

code/routes/basic/app/ts/app.ts

/*
 * Components
 */
import HomeComponent from 'components/HomeComponent';
import AboutComponent from 'components/AboutComponent';
import ContactComponent from 'components/ContactComponent';

现在,让我们看看真正的组件代码

code/routes/basic/app/ts/app.ts

@Component(
  selector: 'router-app',
  directives: [ROUTER_DIRECTIVES],
  template: `
  <div>
    <nav>
      <a>Navigation:</a>
      <ul>
        <li><a [routerLink]="['/Home']">Home</a></li>
        <li><a [routerLink]="['/About']">About</a></li>
        <li><a [routerLink]="['/Contact']">Contact us</a></li>
      </ul>
    </nav>

    <router-outlet></router-outlet>
  </div>
  `
)

在这个组件中,我使用两个路由指令:RouterOutlet和RouterLink。
这些指令和其他的通用路由组件定义在 ROUTER_DIRECTIVES中,当我们导入directive时,我们基本上导入了路由相关的所有指令。

回顾一下,RouterOutlet指令预示在哪里渲染我们的路由内容,使用戴白哦。

RouterLink只用来创建一个链接。

code/routes/basic/app/ts/app.ts

<li><a [routerLink]="['/Home']">Home</a></li>
        <li><a [routerLink]="['/About']">About</a></li>
        <li><a [routerLink]="['/Contact']">Contact us</a></li>

这将会使得angular获得点击事情的权利并根据路由定义导航到特定页面。

接下来,我们使用RouteConfig定义所有的路由

code/routes/basic/app/ts/app.ts

@RouteConfig([
   path: '/', name: 'root', redirectTo: ['/Home'] ,
   path: '/home', name: 'Home', component: HomeComponent ,
   path: '/about', name: 'About', component: AboutComponent ,
   path: '/contact', name: 'Contact', component: ContactComponent ,
   path: '/contactus', name: 'ContactUs', redirectTo: ['/Contact'] ,
])

最后,我们定义RoutesDemoApp

code/routes/basic/app/ts/app.ts

class RoutesDemoApp 

在app.ts的最后,我们启动应用程序。

code/routes/basic/app/ts/app.ts

bootstrap(RoutesDemoApp, [
  ROUTER_PROVIDERS,
  provide(LocationStrategy, useClass: HashLocationStrategy)
]);

上面的代码,不同的地方就是提供了bootstrap的第二个参数,第二个参数是需要注入我们应用程序的一个可注入的数组。

我们需要注入的第一个是ROUTER_PROVIDERS常量,这个常量是所有route 类的数组,像RouteRegistry和Location,它是必须的,对于route的工作来说。

第二个看起来更加复杂

provide(LocationStrategy, useClass: HashLocationStrategy)

让我们深入讲解着第二个参数

路由策略(route strategies)

angular去解析和创建来自路由定义的方式叫路由策略(location strategy)

:fa-info-circle:在angular1中叫路由模式(routing mode)

模式的策略是PathLocationStrategy,它使用的是HTML5路由。当使用这个策略的时候,路由是通过正规路径表示,像/home或者/contact

我们可以改变路由策略,通过将LocationStrategy绑定到一个新的策略,或者直接重新创建一个策略。

代替使用默认的PathLocationStrategy,我们也可以使用HashLocationStrategy.

我们默认使用hash strategy的愿意是因为如果我们使用HTML5策略,我们的路径是正规的。

这种方式,当我们点击一个链接然后跳转的时候,让我们看看从/about到/contact。

如果我们刷新页面,不是像服务器请求跟目录,而是请求的是/about或者/contact,但是,服务器不知道该路径,会返回一个404页面回来。

hash strategy的策略是基于锚标记的,像/#/home 或者 /#/contact,并且,服务器会理解/标签,后面的不用管,这样就能拿到正确的资源(这也是angular1默认使用这个策略的原因)。

:fa-info-circle:让我们看看怎么使用HTML5的模式
为了使用HTML路由模式,你必须配置所有的未知路径到根路径,在routes/basic项目中,我们包含了一个使用webpack-dev-server开发的脚本,它允许使用HTML5的模式。

为了使用它, cd routes/basic 并且运行 node html5-dev-server.js

最后,为了让我们的app使用这个新的策略,我们必须导入LocationStrategy和HashLocationStrategy:

code/routes/basic/app/ts/app.ts

import provide, Component from '@angular/core';
import bootstrap from '@angular/platform-browser-dynamic';
import 
  ROUTER_DIRECTIVES,
  ROUTER_PROVIDERS,
  RouteConfig,
 from '@angular/router-deprecated';

import LocationStrategy, HashLocationStrategy from '@angular/common';

然后,直接加了引用给bootstrap

code/routes/basic/app/ts/app.ts

bootstrap(RoutesDemoApp, [
  ROUTER_PROVIDERS,
  provide(LocationStrategy, useClass: HashLocationStrategy)
]);

:fa-info-circle:如果你想要编写你自己的策略,只需要继承LocationStrategy,并且实现它的接口就可以了。一个好的学习方式就是去阅读HashLocationStrategy和PathLocationStrategy的源码。

Path location strategy

在我们的实例文件夹下,你会发现app/ts/app.html5.ts文件,如果你想要使用默认的路由策略,你只需要拷贝这个文件的内容到app.ts里面就可以了。

运行应用程序

你现在可以进入应用程序根目录(code/routes),并且运行npm run server去启动应用程序。

注意,浏览器的URL已经重定向到了#/home下面。

点击about。

路由参数(route parameter)

在我们的app中,我们通常希望导航到一个特定的资源,比如,我们有一个许多文章的网站,每篇文章有一个id。如果我们有文章的ID,我们可以导航到文章的详情页面,通过下面的URL:

/articles/3

或者ID为4的文章:

/articles/4

等等

很显然,我们不希望对每一个文章都写路由,我们希望使用一个变量或者叫路由参数去标注,我们可以在路由后面加一个冒号,后面添加参数,像这样:

/route/:param

在我们的新闻网站上,我们可能希望这样标注:

/articles/:id

为了增加参数到路由配置上,我们像下面这样写:

code/routes/music/app/ts/app.ts

@RouteConfig([
   path: '/', name: 'root', redirectTo: ['Search'] ,
   path: '/search', name: 'Search', component: SearchComponent ,
   path: '/artists/:id', name: 'Artists', component: ArtistComponent ,
   path: '/tracks/:id', name: 'Tracks', component: TrackComponent ,
   path: '/albums/:id', name: 'Albums', component: AlbumComponent ,
])

当我们访问/artist/123的时候,123将作为参数传递进路由里面去。但是我们怎么去获取这个路由参数呢,

RouteParams

为了使用路由参数,我们首先需要导入RouteParams:

import RouteParams from "@angular/router-deprecated";

然后,将RouteParams注入构造器,让我们看看,有一个路由并且标识在下面:

@RouteConfig([
  path: "/articles/:id", name: "articles", component: ArticleComponent 
 ])

然后,我们编写ArticleComponent,我们增加RouteParams作为组件构造器的参数。

export class ArticleComponent 
 id: string;

 constructor(private routeParams: RouteParams) 
 this.id = routeParams.get("id");
 
 

然后我们传递/articles/230,我们的组件ID属性会接收到230。

Music Search App

让我们编写一个更复杂的应用程序,我们构建一个音乐搜索程序,它有下面这些特征:

  1. 查找更定的音乐
  2. 在一个grid中显示搜索结果
  3. 显示歌手的详细信息
  4. 显示专辑的详细信息
  5. 显示音乐的详细信息,当点击音乐名字的时候,用户可以去播放

这个app会使用下面的路由:

  • /search: 搜索表单和结果
  • /artists/:id:歌手详细信息
  • /albums/:id:专辑详细信息
  • /tracks/:id:歌曲详细信息

第一步

第一件事情就是app.ts,看看它的导入:

code/routes/music/app/ts/app.ts

/*
 * Angular
 */
import 
  Component,
  provide
 from '@angular/core';
import bootstrap from '@angular/platform-browser-dynamic';
import HTTP_PROVIDERS from '@angular/http';
import 

  ROUTER_DIRECTIVES,
  ROUTER_PROVIDERS,
  ROUTER_PRIMARY_COMPONENT,

  Router,
  RouteConfig,
 from '@angular/router-deprecated';
import LocationStrategy, HashLocationStrategy, APP_BASE_HREF from '@angular/common';

然后,我们考虑组件怎么使用每一个路由:

  • 对于Search路由,我们创建一个SearchComponent.,这个组件会告诉Spotify API去执行搜索操作,并且显示结果
  • 对于Albums路由,我们会创建一个AlbumComponent,它会显示album的歌曲列表
  • 对于Tracks路由,我们会创建TrackComponent,它会显示歌曲并且显示歌曲预览

现在,这个组件将于Spotify API进行交互,它看起来需要新建一个服务,然后使用http模块与之交互

让我们首先构建SpotifyService。

SpotifyService

我们将要实现的第一个方法就是searchByTrack,它会通过给定的字符搜索歌曲,具体参照Spotify API的搜索文档,这个节点定义了我们需要的东西:需要一个请求和一个type参数。

下面是第一版:

class SpotifyService 
    constructor(public http:Http) 
    

    searchByTrack(query:string) 
        let params:string = [
            `q=$query`,
            `type=track`
        ].join("&");
        let queryURL:
        `https://api.spotify.com/v1/search?$params`;
        return this.http.request(queryURL).map(res => res.json());
    

这个函数对https://api.spotify.com/v1/search执行一个GET请求,传递我们的search参数和type参数。

所有调用searchByQuery函数的需要订阅Obsevable API,像下面这样:

service
 .searchTrack('query')
 .subscribe((res: any) => console.log('Got object', res))

SearchComponent

既然我们有了搜索歌曲的服务,接下来我们可以编写我们的组件了:

code/routes/music/app/ts/components/SearchComponent.ts

import Component, OnInit from '@angular/core';
import CORE_DIRECTIVES from '@angular/common';
import 
  Router,
  RouterLink,
  RouteParams,
 from '@angular/router-deprecated';

/*
 * Services
 */
import SpotifyService from 'services/SpotifyService';

这里,我们导入通用的内容,然后注入我们刚创建的SpotifyService。

code/routes/music/app/ts/components/SearchComponent.ts

@Component(
  selector: 'search',
  directives: [RouterLink, CORE_DIRECTIVES],
  template: `
  <h1>Search</h1>

  <p>
    <input type="text" #newquery
      [value]="query"
      (keydown.enter)="submit(newquery.value)">
    <button (click)="submit(newquery.value)">Search</button>
  </p>

  <div *ngIf="results">
    <div *ngIf="!results.length">
      No tracks were found with the term ' query '
    </div>

    <div *ngIf="results.length">
      <h1>Results</h1>

      <div class="row">
        <div class="col-sm-6 col-md-4" *ngFor="let t of results">
          <div class="thumbnail">
            <div class="content">
              <img src=" t.album.images[0].url " class="img-responsive">
              <div class="caption">
                <h3>
                  <a [routerLink]="['/Artists', id: t.artists[0].id]">
                     t.artists[0].name 
                  </a>
                </h3>
                <br>
                <p>
                  <a [routerLink]="['/Tracks', id: t.id]">
                     t.name 
                  </a>
                </p>
              </div>
              <div class="attribution">
                <h4>
                  <a [routerLink]="['/Albums', id: t.album.id]">
                     t.album.name 
                  </a>
                </h4>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
  `
)

然后,我们的目标就是像下面这样渲染歌曲:

搜索字段

让我们分解模板,第一部分就是一个搜索域。

code/routes/music/app/ts/components/SearchComponent.ts

<p>
    <input type="text" #newquery
      [value]="query"
      (keydown.enter)="submit(newquery.value)">
    <button (click)="submit(newquery.value)">Search</button>
  </p>

这里,我们有一个输入框,并且绑定它的值到我们组件的query属性。我们也给这个input一个模板变量名字:#newquery.,我们在组件中使用newquery.value访问input的值。

这个button将会出发组件的submit函数,将input的值作为参数传递进去。

当敲击enter的时候,submit也会被触发。

搜索结果和链接

接下来就是显示结果,我们可以使用ngFor去迭代每一个result对象

code/routes/music/app/ts/components/SearchComponent.ts

<div class="row">
 <div class="col-sm-6 col-md-4" *ngFor="let t of results">

对于每一个歌曲,我们显示歌手名字:

code/routes/music/app/ts/components/SearchComponent.ts

<h3>
<a [routerLink]="['/Artists', id: t.artists[0].id]">
 t.artists[0].name 

注意,我们使用RouterLink去定向[‘/artists’, id: t.artists[0].id].
这就是我们设置给定route的参数的方式,下面你会看到id这个值在一个合适的组件中被处理。
然后显示歌曲:

code/routes/music/app/ts/components/SearchComponent.ts

<p>
<a [routerLink]="['/Tracks', id: t.id]">
  t.name 

以及专辑

code/routes/music/app/ts/components/SearchComponent.ts

<h4>
 <a [routerLink]="['/Albums', id: t.album.id]">
  t.album.name 

SearchComponent类

让我们看看它的构造函数
code/routes/music/app/ts/components/SearchComponent.ts

export class SearchComponent implements OnInit 
  query: string;
  results: Object;

  constructor(public spotify: SpotifyService, public router: Router,
              public routeParams: RouteParams) 
  

这里定义了两个属性:

  • query:代表当前输入
  • results:代表搜索结果

构造器中注入了SpotifyService、Router和RouteParams。

为了执行搜索操作,我们定义了search函数
code/routes/music/app/ts/components/SearchComponent.ts

search(): void 
    this.query = this.routeParams.get('query');
    if (!this.query) 
      return;
    

    this.spotify
      .searchTrack(this.query)
      .subscribe((res: any) => this.renderResults(res));
  

我们希望获取query参数,如果参数是空的不进行搜索。

然后调用searchTrack去搜索歌曲,订阅结果,最后显示结果通过renderResults

code/routes/music/app/ts/components/SearchComponent.ts

renderResults(res: any): void 
    this.results = null;
    if (res && res.tracks && res.tracks.items) 
      this.results = res.tracks.items;
    
  

我们定义了一个results的属性,不管它何时更新,angular都会告知view去更新。

页面加载的时候搜索

当页面加载的时候,我们进行一次搜索:

code/routes/music/app/ts/components/SearchComponent.ts

ngOnInit(): void 
    this.search();
  

submit

让我们看看,当我们提交一个表单的时候做了什么?
code/routes/music/app/ts/components/SearchComponent.ts

submit(query: string): void 
    this.router.navigate(['/Search', query: query]);
    this.search();
  

我们会手动导航到搜索栏,然后执行一个搜索操作。做这个事情的意义就是,当浏览器重新加载的时候,我们会看到相同的搜索结果。

合起来

code/routes/music/app/ts/components/SearchComponent.ts

/*
 * Angular
 */

import Component, OnInit from '@angular/core';
import CORE_DIRECTIVES from '@angular/common';
import 
  Router,
  RouterLink,
  RouteParams,
 from '@angular/router-deprecated';

/*
 * Services
 */
import SpotifyService from 'services/SpotifyService';

@Component(
  selector: 'search',
  directives: [RouterLink, CORE_DIRECTIVES],
  template: `
  <h1>Search</h1>

  <p>
    <input type="text" #newquery
      [value]="query"
      (keydown.enter)="submit(newquery.value)">
    <button (click)="submit(newquery.value)">Search</button>
  </p>

  <div *ngIf=(播放框架2.4)在客户端反向路由图像?

在 React 中循环数据以创建“动态路由”

Mycat的分库功能的路由查找(计算)的实现

浏览器地址栏按回车发生了什么事情

浏览器窗口输入网址后发生的一段事情(http完整请求)

我是不是应该期望 C++ 编译器会编译具有“按编码”的数据竞争的多线程代码,或者它可能会做其他事情?