AngularDart4.0 英雄之旅-教程-07路由 顶

Tour of Heroes应用程序有新的要求:

  • 添加一个Dashboard视图。
  • 添加在Heroes和Dashboard视图之间导航的功能。
  • 当用户在任一视图中点击英雄名称时,导航至所选英雄的详细视图。
  • 当用户点击电子邮件中的深层链接时,打开特定英雄的详细视图。

完成后,用户将可以像这样浏览应用程序:

为了满足这些要求,您将添加Angular路由器到应用程序。

有关路由器的更多信息,请阅读路由和导航页面。

 当你完成这个页面,应用程序应该看起来像这个实例(查看源代码)。

开始阶段

在继续英雄之旅之前,请确认您具有以下结构。

如果该应用程序尚未运行,请启动该应用程序。 在进行更改时,请通过重新加载浏览器窗口来保持运行。

行动计划

计划如下:

  • 将AppComponent转换为仅处理导航的应用程序外壳程序。
  • 将当前AppComponent中的英雄相关的重新定位到单独的HeroesComponent。
  • 添加路由。
  • 创建一个新的DashboardComponent。
  • 将Dashboard绑定到导航结构中。

路由是导航的另一个名称。 路由是导航从视图到视图的机制。

分割AppComponent

当前的应用程序加载AppComponent并立即显示英雄列表。 修改后的应用程序应该提供一个可选的视图(Dashboard和Heroes),然后默认为其中的一个。

AppComponent只应该处理导航,所以你可以将Heroes的显示从AppComponent移出并放到它自己的HeroesComponent中。

HeroesComponent

AppComponent已经专注于英雄。 将代码移出AppComponent,将其重命名为HeroesComponent,并创建一个单独的AppComponent外壳。

请执行下列操作:

  • 重命名并将app_component.*文件移动到src / heroes_component.*。
  • 从导入路径中删除src /前缀。
  • 将AppComponent类重命名为HeroesComponent(仅在本地重命名,仅在此文件中)。
  • 将选择器my-app重命名为my-heroes。
  • 将模板URL更改为heroes_component.html,并将样式文件更改为heroes_component.css。

lib/src/heroes_component.dart (showing renamings only) 

@Component(
  selector: 'my-heroes',
  templateUrl: 'heroes_component.html',
  styleUrls: const ['heroes_component.css'],
)
class HeroesComponent implements OnInit {
  HeroesComponent(
      this._heroService,
      );
}

创建AppComponent

新的AppComponent是应用程序外壳。 它将在顶部有一些导航链接,下面有一个显示区域。

执行这些步骤:

  • 创建文件lib / app_component.dart。
  • 定义一个AppComponent类。
  • 使用my-app选择器在类的上方添加@Component注解。
  • 将以下英雄组件移到AppComponent:
  •  title类属性。
  • @Component 模板<h1>节点,其中包含对title的绑定。
  • 将HeroesComponent添加到AppComponent的指令列表中,以便Angular识别<my-heroes>标签。
  • 将HeroService添加到AppComponent的providers 列表中,因为在其他所有视图中都需要它。
  • 从升级后的HeroesComponent providers列表中删除HeroService。
  • 为AppComponent添加支持导入语句。

第一稿看起来是这样的:lib/app_component.dart

import 'package:angular/angular.dart';

import 'src/hero_service.dart';
import 'src/heroes_component.dart';

@Component(
  selector: 'my-app',
  template: '''
    <h1>{{title}}</h1>
    <my-heroes></my-heroes>
  ''',
  directives: const [HeroesComponent],
  providers: const [HeroService],
)
class AppComponent {
  final title = 'Tour of Heroes';
}

刷新浏览器。 该应用程序仍然运行并显示英雄。

添加路由

应该在用户点击按钮后显示英雄而不是自动显示。 换句话说,用户应该能够导航到英雄列表。

更新pubspec

使用Angular路由(angular_router)启用导航。 由于路由器在自己的包中,首先将该包添加到应用的pubspec:

并非所有的应用程序都需要路由,这就是为什么Angular路由器处于独立的可选软件包中的原因。

导入库

Angular路由器是多个服务(ROUTER_PROVIDERS)、指令(ROUTER_DIRECTIVES)和配置类的组合。 你可以通过导入路由库来得到它们:lib/app_component.dart (router import)

import 'package:angular_router/angular_router.dart';

使路由器可用

要告诉Angular您的应用使用路由,请在应用的引导程序功能中指定ROUTER_PROVIDERS:web/main.dart

import 'package:angular/angular.dart';
import 'package:angular_router/angular_router.dart';
import 'package:angular_tour_of_heroes/app_component.dart';

void main() {
  bootstrap(AppComponent, [
    ROUTER_PROVIDERS,
    // Remove next line in production
    provide(LocationStrategy, useClass: HashLocationStrategy),
  ]);
}

使用哪个位置策略 默认的LocationStrategyPathLocationStrategy,所以在生产中,可以使用ROUTER_PROVIDERS,而不必使用LocationStrategy提供程序。 在开发过程中,使用HashLocationStrategy更方便,因为pub serve不支持deep linking。 有关详细信息,请参阅位置策略的LocationStrategy and browser URL styles

接下来,将ROUTER_DIRECTIVES添加到@Component注解中,并删除HeroesComponent: 

lib/app_component.dart (directives)

directives: const [ROUTER_DIRECTIVES],

您可以从指令列表中移除HeroesComponent,因为AppComponent不会直接显示英雄; 这是路由器的工作。 很快你会从模板中删除<my-heroes>。

<base href>

打开index.html并确保在<head>部分的顶部有一个<base href =“...”>元素(或者一个动态设置这个元素的脚本)。

正如在“Routing and Navigation”页面的“ Set the base href”部分所述,示例应用程序使用以下脚本:

web/index.html (base-href)

<head>
  <script>
    // WARNING: DO NOT set the <base href> like this in production!
    // Details: https://webdev.dartlang.org/angular/guide/router
    (function () {
      var m = document.location.pathname.match(/^(\/[-\w]+)+\/web($|\/)/);
      document.write('<base href="' + (m ? m[0] : '/') + '" />');
    }());
  </script>

配置路由

Routes 告诉路由当用户点击一个链接或者将一个URL粘贴到浏览器地址栏中时显示哪些视图。

创建一个路由配置(RouteConfig)来保存应用程序路由定义的列表。 定义第一个路由作为到英雄组件的路由。lib/app_component.dart (Heroes route)

@RouteConfig(const [
  const Route(path: '/heroes', name: 'Heroes', component: HeroesComponent)
])

路由定义是一个具有以下命名参数的Route对象:

  • path:路由器将此字符串与浏览器地址栏(/ heroes)中的URL匹配。
  • name:路线名称(Heroes)。 它必须以大写字母开头以避免与路径混淆。
  • component(组件):此路由导航时到(HeroesComponent)时将被激活的组件。

路由和导航页面阅读更多关于定义路由的信息。

Router outlet

如果您访问localhost:8080/#/heroes,路由器应该匹配英雄路线的URL并显示一个HeroesComponent。 但是,您必须告诉路由器在哪里显示组件。 为此,在模板的末尾添加一个<router-outlet>元素。 RouterOutlet是ROUTER_DIRECTIVES之一。 当用户通过应用程序导航时,路由器会在<router-outlet>正下方显示每个组件。 刷新浏览器,然后访问localhost:8080 /#/ heroes。 你应该看到英雄列表。

路由链接

用户不必粘贴路由路径到地址栏。 相反,向模板添加一个锚点,点击后会触发到HeroesComponent的导航。

修改后的模板如下所示:lib/app_component.dart (template)

template: '''
  <h1>{{title}}</h1>
  <a [routerLink]="['Heroes']">Heroes</a>
  <router-outlet></router-outlet>
''',

 请注意锚标记中的[routerLink]绑定。 RouterLink指令告诉路由在用户点击链接时的位置。

您使用链接参数列表定义了一个路由指令, 这个列表在我们的小样本中只有一个元素,引用的路由名称。 回头看看路由配置,确定“Heroes”是到HeroesComponent的路由的名字。

了解路由章节中的链接参数列表。

刷新浏览器,浏览器显示应用标题和英雄链接,但不是英雄列表。点击英雄导航链接。地址栏更新为 /#/heroes(或同等/#heroes),英雄列表显示。

AppComponent现在看起来像这样:lib/app_component.dart

import 'package:angular/angular.dart';
import 'package:angular_router/angular_router.dart';

import 'src/hero_service.dart';
import 'src/heroes_component.dart';

@Component(
  selector: 'my-app',
  template: '''
    <h1>{{title}}</h1>
    <a [routerLink]="['Heroes']">Heroes</a>
    <router-outlet></router-outlet>
  ''',
  directives: const [ROUTER_DIRECTIVES],
  providers: const [HeroService],
)
@RouteConfig(const [
  const Route(path: '/heroes', name: 'Heroes', component: HeroesComponent)
])
class AppComponent {
  final title = 'Tour of Heroes';
}

AppComponent有一个路由,并显示路由视图。 为此,为了区别于其他类型的组件,这种组件类型称为路由组件。

添加一个仪表板

只有当多个视图存在时,路由才有意义。 要添加另一个视图,请创建一个占位DashboardComponent。

lib/src/dashboard_component.dart (v1)

import 'package:angular/angular.dart';

@Component(
  selector: 'my-dashboard',
  template: '<h3>My Dashboard</h3>',
)
class DashboardComponent {}

稍后您将使这个组件更加有用。

配置dashboard 路由

添加一个类似Heroes路由的dashboard 路由:lib/app_component.dart (Dashboard route)

const Route(
  path: '/dashboard',
  name: 'Dashboard',
  component: DashboardComponent,
),

添加一个重定向路由

目前,浏览器在/在地址栏中启动。 当应用程序启动时,它应该显示仪表板,并在地址栏中显示路径 /#/dashboard 。

要做到这一点,请添加重定向路由:lib/app_component.dart (Redirect route)

const Redirect(path: '/', redirectTo: const ['Dashboard']),

或者,您可以将dashboard 定义为默认路由。 在路由和导航页面阅读有关默认路由和重定向的更多信息。

将导航添加到dashboard 

在模板上添加dashboard 导航链接,在heroes链接上方。lib/app_component.dart (template)

template: '''
  <h1>{{title}}</h1>
  <nav>
    <a [routerLink]="['Dashboard']">Dashboard</a>
    <a [routerLink]="['Heroes']">Heroes</a>
  </nav>
  <router-outlet></router-outlet>
''',

<nav>标签目前还没有做任何事情,但是当您格式化链接时,它们会很有用。

在浏览器中,转至应用程序根目录(/)并重新加载。 该应用程序显示dashboard ,您可以在dashboard 和heroes之间导航。 

将heroes添加到dashboard 

为了让dashboard 更有趣,您一眼就可以看到前四名的英雄。

将template元数据替换为指向新模板文件的templateUrl属性,并添加如下所示的指令(还要添加必要的导入):lib/src/dashboard_component.dart (metadata)

@Component(
  selector: 'my-dashboard',
  templateUrl: 'dashboard_component.html',
  directives: const [CORE_DIRECTIVES, ROUTER_DIRECTIVES],
)

templateUrl的值可以是这个包或其他包中的asset。 要在另一个包中使用资源,请使用完整的包引用,如“package:some_other_package / dashboard_component.html”。

使用以下内容创建模板文件:lib/src/dashboard_component.html

<h3>Top Heroes</h3>
<div class="grid grid-pad">
  <div *ngFor="let hero of heroes">
    <div class="module hero">
      <h4>{{hero.name}}</h4>
    </div>
  </div>
</div>

* ngFor再次用于遍历英雄列表并显示他们的名字。 额外的<div>元素将有助于以后的格式化样式。

共享HeroService

要填充组件的英雄列表,您可以重新使用HeroService。

之前,您从HeroesComponent的提供程序列表中删除了HeroService,并将其添加到AppComponent的提供程序列表中。 这个举动创建了一个单例HeroService实例,可用于应用程序的所有组件。 Angular注入HeroService,您可以在DashboardComponent中使用它。

获取英雄

在dashboard_component.dart中,添加以下导入语句。lib/src/dashboard_component.dart (imports)

import 'dart:async';

import 'package:angular/angular.dart';
import 'package:angular_router/angular_router.dart';

import 'hero.dart';
import 'hero_service.dart';

现在创建DashboardComponent类,如下所示:lib/src/dashboard_component.dart (class)

class DashboardComponent implements OnInit {
  List<Hero> heroes;

  final HeroService _heroService;

  DashboardComponent(this._heroService);

  Future<Null> ngOnInit() async {
    heroes = (await _heroService.getHeroes()).skip(1).take(4).toList();
  }
}

HeroesComponent也使用这种逻辑:

  • 定义一个英雄列表属性。
  • 在构造函数中注入HeroService,并将其保存在一个专用的_heroService字段中。
  • 调用服务来获取Angular ngOnInit()生命周期钩子中的英雄。

在这个仪表板中你指定了四个英雄(第二,第三,第四和第五)。

刷新浏览器以查看新仪表板中的四个英雄名称。

导航到英雄的细节

虽然所选英雄的详细信息显示在HeroesComponent的底部,但用户应该能够通过以下其他方式导航到HeroDetailComponent:

  • 从仪表板到选定的英雄。
  • 从英雄名单到选定的英雄。
  • 从“深层链接”网址粘贴到浏览器地址栏中。

路由到英雄细节

您可以在AppComponent中添加到HeroDetailComponent的路由,其中定义了其他路由。

新的路由是不寻常的,你必须告诉HeroDetailComponent显示哪个英雄。 您不必告诉HeroesComponent或DashboardComponent任何东西。

目前,父HeroesComponent使用如下绑定将组件的hero属性设置为hero对象:

<hero-detail [hero]="selectedHero"></hero-detail>

但是这种绑定在任何路由脚本中都不起作用。

参数化的路由

您可以将英雄的id添加到路由路径。 当路由到英雄的id为11,你可以期望看到这样的路径:

/detail/11

/ detail /部分是不变的。 尾随的数字id在英雄与英雄间变换。 您需要使用代表英雄id的参数来表示路由的可变部分。

添加一个附带参数的路由

首先,导入英雄细节组件:

import 'src/hero_detail_component.dart';

接下来,添加以下路由:lib / app_component.dart(HeroDetail route)

const Route(
  path: '/detail/:id',
  name: 'HeroDetail',
  component: HeroDetailComponent,
),

路径中的冒号(:)表示:id在导航到HeroDetailComponent时是特定英雄id的占位符。

你已经完成了应用程序的路由。

您没有向模板添加英雄详情链接,因为用户单击导航链接不是为了查看特定的英雄; 而是点击一个英雄的名字,不管名字是显示在仪表板还是英雄列表中。 但是,直到HeroDetailComponent被修改并准备好导航到这个时候,它才会起作用。

修改HeroDetailComponent

以下是HeroDetailComponent现在的样子:lib/src/hero_detail_component.dart (current)

import 'package:angular/angular.dart';
import 'package:angular_forms/angular_forms.dart';
import 'hero.dart';
@Component(
  selector: 'hero-detail',
  template: '''
    <div *ngIf="hero != null">
      <h2>{{hero.name}} details!</h2>
      <div><label>id: </label>{{hero.id}}</div>
      <div>
        <label>name: </label>
        <input [(ngModel)]="hero.name" placeholder="name"/>
      </div>
    </div>
  ''',
  directives: const [CORE_DIRECTIVES, formDirectives],
)
class HeroDetailComponent {
  @Input()
  Hero hero;
}

该模板不会改变。 英雄的名字将显示相同的方式。 主要的变化是如何得到英雄的名字。

您将不再接收父组件属性绑定中的英雄,因此您可以从hero字段中删除@Input()注解:lib/src/hero_detail_component.dart (hero with @Input removed)

class HeroDetailComponent implements OnInit {
  Hero hero;
}

新的HeroDetailComponent将从路由器的RouteParams服务中获取id参数,并使用HeroService来获取具有该id的英雄。

添加以下导入:lib/src/hero_detail_component.dart (added-imports)

import 'dart:async';

import 'package:angular_router/angular_router.dart';

import 'hero_service.dart';

RouteParams,HeroService和Location服务注入到构造函数中,并将其值保存在私有字段中:lib/src/hero_detail_component.dart (constructor)

final HeroService _heroService;
final RouteParams _routeParams;
final Location _location;

HeroDetailComponent(this._heroService, this._routeParams, this._location);

告诉类实现OnInit接口。

class HeroDetailComponent implements OnInit {

在ngOnInit()生命周期的钩子中,从RouteParams服务中提取id参数值,并使用HeroService来获取具有该id的英雄。lib/src/hero_detail_component.dart (ngOnInit)

Future<Null> ngOnInit() async {
  var _id = _routeParams.get('id');
  var id = int.parse(_id ?? '', onError: (_) => null);
  if (id != null) hero = await (_heroService.getHero(id));
}

注意如何通过调用RouteParams.get()方法来提取id。

英雄id是一个数字。 路由参数始终是字符串。 所以路由参数值被转换成一个数字。

添加HeroService.getHero()

在ngOnInit()中,你使用了HeroService还没有的getHero()方法。 要解决这个问题,打开HeroService,并添加一个getHero()方法,通过id从getHeroes()过滤英雄列表。lib/src/hero_service.dart (getHero)

Future<Hero> getHero(int id) async =>
    (await getHeroes()).firstWhere((hero) => hero.id == id);

找回路

用户有几种方式导航到HeroDetailComponent。

要在其他地方导航,用户可以单击AppComponent中的两个链接之一,或单击浏览器的后退按钮。 现在添加第三个选项,一个goBack()方法,使用您之前注入的Location服务在浏览器的历史堆栈中向后导航一步。

lib/src/hero_detail_component.dart (goBack)

void goBack() => _location.back();

回头太远可能会把用户带出应用程序。 在一个真正的应用程序中,您可以使用routerCanDeactivate()挂钩来防止此问题。 在CanDeactivate页面上阅读更多信息。

 您将使用绑定到后退按钮的事件连接此方法,您将添加到组件模板。

<button (click)="goBack()">Back</button>

将模板迁移到名为hero_detail_component.html的文件:lib/src/hero_detail_component.html

<div *ngIf="hero != null">
  <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>

更新组件templateUrl元数据指向您刚刚创建的模板文件。lib/src/hero_detail_component.dart (metadata)

@Component(
  selector: 'hero-detail',
  templateUrl: 'hero_detail_component.html',
  directives: const [CORE_DIRECTIVES, formDirectives],
)

刷新浏览器并访问localhost:8080/#details/11. 应该显示英雄11的详细信息。 在仪表板或英雄列表中选择英雄不起作用。 你会接下来的处理。

选择一个仪表板英雄

当用户选择仪表板中的英雄时,应用程序应该导航到HeroDetailComponent以允许用户查看和编辑选择的英雄。

仪表板英雄的行为应该像锚标签:当悬停在英雄的名字,目标网址应该显示在浏览器的状态栏,用户应该能够复制链接或在新标签打开英雄详细信息视图。

为了达到这个效果,打开dashboard_component.html并用一个锚点替换<div * ngFor ...>元素(子元素保持不变):lib/src/dashboard_component.html (repeated <a> tag)

<a *ngFor="let hero of heroes" [routerLink]="['HeroDetail', {id: hero.id.toString()}]" class="col-1-4">
  <div class="module hero">
    <h4>{{hero.name}}</h4>
  </div>
</a>

注意[routerLink]绑定。 如本页“路由链接”部分所述,AppComponent模板中的顶级导航将路由器链接设置为目标路由,/dashboard 和/ heroes的固定名称。

这次,您绑定到包含链接参数列表的表达式。 该列表包含两个元素:目标路由的名称和设置为当前英雄id值的路由参数。

这两个列表项与您之前添加的参数化英雄细节路由定义中的名称和:id相对应:lib/app_component.dart (HeroDetail route)

const Route(
  path: '/detail/:id',
  name: 'HeroDetail',
  component: HeroDetailComponent,
),

刷新浏览器并从仪表板中选择一个英雄; 该应用程序导航到该英雄的细节。

在HeroesComponent中选择一个英雄

在HeroesComponent中,当前的模板展示了一个“主/细节”风格,顶部是英雄列表,下方是选定的英雄的详细信息。lib/src/heroes_component.html

<h2>My Heroes</h2>
<ul class="heroes">
  <li *ngFor="let hero of heroes"
      [class.selected]="hero === selectedHero"
      (click)="onSelect(hero)">
    <span class="badge">{{hero.id}}</span> {{hero.name}}
  </li>
</ul>
<hero-detail [hero]="selectedHero"></hero-detail>

你不会再在这里显示完整的HeroDetailComponent。 相反,您将在自己的页面上显示英雄细节,并按照您在仪表板中所做的方式路由到它。 进行这些更改:

  • 从模板的最后一行删除<hero-detail>元素。
  • 从指令列表中删除HeroDetailComponent。
  • 删除英雄细节导入。

当用户从列表中选择一个英雄时,他们不会进入详细页面。 相反,他们会在此页面上看到一个迷你细节,并且必须单击一个按钮才能导航到完整的详细信息页面。

添加迷你细节

在模板底部添加以下HTML片段,在<hero-detail>之前的地方:lib/src/heroes_component.html (mini detail)

<div *ngIf="selectedHero != null">
  <h2>
    {{selectedHero.name | uppercase}} is my hero
  </h2>
  <button (click)="gotoDetail()">View Details</button>
</div>

点击一个英雄(但不要现在尝试,因为它不会工作),用户应该在英雄列表下面看到这样的东西:

由于管道运算符(|)之后的插值绑定中包含的uppercase管道,英雄的名称将以大写字母显示。

{{selectedHero.name | uppercase}} is my hero

管道是格式化字符串,货币金额,日期和其他显示数据的好方法。 有几个管道是已提供的,你可以写你自己的。

警告在模板中使用Angular管道之前,需要将其列在组件的@Component注解的pipes参数中。 您可以单独添加管道,或者为了方便起见,可以使用COMMON_PIPES组。lib/src/heroes_component.dart (pipes)

@Component(
  selector: 'my-heroes',
  pipes: const [COMMON_PIPES],
)

在“Pipes”页面上阅读有关管道的更多信息。

刷新浏览器。 从英雄列表中选择英雄将激活迷你细节视图。 查看详细信息按钮不起作用。

更新HeroesComponent类

响应按钮单击,HeroesComponent导航到HeroesDetailComponent。 按钮的点击事件绑定到一个gotoDetail()方法,该方法应该通过告诉路由器去哪里命令性地导航。

这种方法需要对组件类进行以下更改:

  • 导入angular_router。
  • 在构造函数中注入路由器,以及HeroService。
  • 通过调用路由器的navigate()方法来实现gotoDetail()。

这里是修改后的HeroesComponent类:lib/src/heroes_component.dart (class)

class HeroesComponent implements OnInit {
  final HeroService _heroService;
  final Router _router;
  List<Hero> heroes;
  Hero selectedHero;

  HeroesComponent(
      this._heroService,
      this._router
      );

  Future<Null> getHeroes() async {
    heroes = await _heroService.getHeroes();
  }

  void ngOnInit() => getHeroes();

  void onSelect(Hero hero) => selectedHero = hero;

  Future<Null> gotoDetail() => _router.navigate([
        'HeroDetail',
        {'id': selectedHero.id.toString()}
      ]);
}

在gotoDetail()中,你正在向路由器的navigate()方法传递一个两元素链接参数列表(一个名字和路由参数),就像你在DashboardComponent中的[routerLink]绑定中一样。

刷新浏览器并开始点击。 用户可以在应用程序周围进行导航,从仪表板到英雄详细信息,然后返回,从英雄列表到英雄详细信息,再次回到英雄。

你已经达到推动这个页面的所有导航要求。

风格化应用程序

该应用程序是功能,但它需要样式。 仪表板英雄应显示在一排矩形。 为此目的,您已经收到了大约60行CSS,包括一些简单的媒体查询响应式设计。

正如您现在所知,将CSS添加到组件样式元数据将会隐藏组件逻辑。 相反,您将添加CSS来分隔.css文件。

Dashboard 样式

在lib / src文件夹中创建一个dashboard_component.css文件,并在组件元数据的styleUrls列表属性中引用该文件,如下所示:

lib / src/dashboard_component.dart(styleUrls)

@Component(
  selector: 'my-dashboard',
  templateUrl: 'dashboard_component.html',
  styleUrls: const ['dashboard_component.css'],
  directives: const [CORE_DIRECTIVES, ROUTER_DIRECTIVES],
)

lib / src/dashboard_component.css

[class*='col-'] {
  float: left;
  text-decoration: none;
  padding-right: 20px;
  padding-bottom: 20px;
}
[class*='col-']:last-of-type {
  padding-right: 0;
}
*, *: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;
	}
}

英雄细节风格

在lib / src文件夹中创建一个hero_detail_component.css文件,并在组件元数据的styleUrls列表中引用该文件:

lib / src/hero_detail_component.dart(styleUrls)

@Component(
  selector: 'hero-detail',
  templateUrl: 'hero_detail_component.html',
  styleUrls: const ['hero_detail_component.css'],
  directives: const [CORE_DIRECTIVES, formDirectives],
)

lib / src/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;
}

设置导航链接样式

在lib文件夹中创建一个app_component.css文件,并在组件元数据的styleUrls列表中引用该文件:

lib / app_component.dart(styleUrls)

styleUrls: const ['app_component.css'],

lib / 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.router-link-active {
  color: #039be5;
}

提供的CSS使AppComponent中的导航链接更像可选按钮。 早些时候,你用<nav>元素包围了这些链接:

router-link-active 类 Angular路由器将router-link-active类添加到其路由与活动路由相匹配的HTML导航元素。 你所要做的就是定义它的风格。

应用程序全局样式

将样式添加到组件时,可以将组件需要的所有内容(HTML,CSS和代码)一起放在一个方便的位置。 把它打包起来很容易,在其他地方重新使用组件。

您还可以在任何组件之外的应用程序级别创建样式。

设计师提供了一些基本样式来应用于整个应用程序的元素。 这些对应于您在安装期间先前安装的全套主样式。 这是一个摘录:web/styles.css (excerpt)

@import url(https://fonts.googleapis.com/css?family=Roboto);
@import url(https://fonts.googleapis.com/css?family=Material+Icons);

/* 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;
}

如有必要,创建文件web / styles.css。 确保文件包含此处提供的主要样式。 另外编辑web / index.html来引用这个样式表。

web / index.html(link ref)

<link rel="stylesheet" href="styles.css">

现在看看应用程序。 仪表板,英雄和导航链接的样式。

应用程序结构和代码

查看此页面的实例(查看源代码)中的示例源代码。 确认您具有以下结构:

你走过的路

以下是您在此页面中所取得的成果:

  • 您添加了Angular路由器来浏览不同的组件。
  • 您了解了如何创建路由器链接来表示导航菜单项。
  • 您使用路由器链接参数导航到用户选择的英雄的细节。
  • 您在多个组件之间共享HeroService。
  • 您添加了uppercase管道来格式化数据。

你的应用应该看起来像这个实例(查看源代码)。

前方的路 你有很多基础,你需要建立一个应用程序。 您仍然缺少一个关键部分:远程数据访问。

在下一页中,您将使用http从服务器检索到的数据替换模拟数据。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Coding迪斯尼

Reactjs+BootStrap开发自制编程语言Monkey的编译器:创建简易的页面IDE

18820
来自专栏jianhuicode

那些React-Native踩过的的坑

    这几天开始边学边做新模式,也踩了不少坑,所以会记录下来--俗话说好记心不如烂笔头,何况还没有一颗好记心(-_-)。    从学React-Native开...

23290
来自专栏企鹅号快讯

5 款最酷的 Linux 终端模拟器

Xiki 首先我要推荐的第一个终端是 Xiki。 Xiki 是 Craig Muth 的智慧结晶,他是一个天才程序员,也是一个有趣的人(有趣在此处的意思是幽默,...

467100
来自专栏Web 开发

Deploy WordPress On SAE

也没怎么刻意去选择,从各种cPanel主机,到VPS,再到AppFog等Pass,基本上都玩了一遍了。

9800
来自专栏前端之路

–[New]Vue项目使用vw实现移动端适配教程

21830
来自专栏BestSDK

一文读懂Xcode 9 所有更新:全新模拟器,兼容Swift低版本等

代码重构功能增强 这次 Xcode 9 对代码重构功能有较大的提升,首先是重命名功能,在编辑器中点击类名,然后选择 Refactor -> Rename: ?...

46070
来自专栏PHP在线

表单提交刷新页面问题

今天开发中遇到了一个问题,刚开始没有头绪,不知道怎样解决,后来静下来一想,搜索下吧,经过搜索相关资料,很好的解决。 <form name="keywordFor...

37460
来自专栏冰霜之地

大话大前端时代(一) —— Vue 与 iOS 的组件化

今年大前端的概念一而再再而三的被提及,那么大前端时代究竟是什么呢?大前端这个词最早是因为在阿里内部有很多前端开发人员既写前端又写 Java 的 Velocity...

18930
来自专栏大数据钻研

前端开发面试题总结之——HTML

---- 相关知识点 web标准、 web语义化、 浏览器内核、 兼容性、 html5... 题目&答案 Doctype作用?严格模式与混杂模式如何区分?它们有...

36980
来自专栏地方网络工作室的专栏

Python3 初学实践案例(7)tkinter 入门 GUI 的密码生成程序

Python3 初学实践案例(7)tkinter 入门 GUI 的密码生成程序 前面我已经非常好的完成了 cli 的密码生成程序的编写 http://blog....

29480

扫码关注云+社区

领取腾讯云代金券