flask框架视图和路由

Angular vs AngularJS (Angular vs AngularJS)

AngularJS (versions 1.x) is a JavaScript-based open source framework. It is cross platform and is used to develop Single Page Web Application (SPWA).

AngularJS(版本1.x)是一个基于JavaScript的开源框架。 它是跨平台的,用于开发单页Web应用程序(SPWA)。

AngularJS implements the MVC pattern to separate the logic, presentation, and data components. It also uses dependency injection to make use of server-side services in client side applications.

AngularJS实现了MVC模式以分离逻辑,表示和数据组件。 它还使用依赖注入来在客户端应用程序中利用服务器端服务。

Angular (versions 2.x and up) is a Typescript-based open source framework used to develop front-end web applications. Angular has the following features like generics, static-typing, dynamic loading, and also some ES6 features.

Angular(2.x及更高版本)是基于Typescript的开源框架,用于开发前端Web应用程序。 Angular具有以下功能,例如泛型,静态键入,动态加载以及某些ES6功能。

版本记录 (Version History)

Google released the initial version of AngularJS on October 20,2010. The first stable release of AngularJS was on December 18, 2017 of version 1.6.8.

Google于2010年10月20日发布了AngularJS的初始版本。 AngularJS的第一个稳定版本于1.6.8版于2017年12月18日发布。

The Angular 2.0 release took place on September 22 2014 at the ng-Europe conference.

Angular 2.0版本于2014年9月22日在ng-Europe会议上发布。

After some modifications, Angular 4.0 was released in December 2016. Angular 4 is backward compatible with Angular 2.0. The HttpClient library is one of the new features of Angular 4.0.

经过一些修改,Angular 4.0于2016年12月发布。Angular4向后兼容Angular 2.0。 HttpClient库是Angular 4.0的新功能之一。

Angular 5 release was on November 1, 2017. Support for progressive web apps (PWAs)  was one of the improvements to Angular 4.0.

Angular 5发布于2017年11月1日。对渐进式Web应用程序(PWA)的支持是对Angular 4.0的改进之一。

And finally, Angular 6 was released in May 2018. The latest stable version is 6.1.9

最后,Angular 6于2018年5月发布。最新的稳定版本是6.1.9。

如何安装 (How to install it)

We can add Angular either by referencing the sources available or downloading the framework.

我们可以通过引用可用的源代码或下载框架来添加Angular。

AngularJS: We can add AngularJS (Angular 1.x versions) by referencing the Content Delivery Network from Google.

AngularJS:我们可以通过引用Google的内容交付网络来添加AngularJS(Angular 1.x版本)。

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>

Download/install: We can download the framework with npm, Bower, or composer

下载/安装:我们可以使用npm,Bower或composer下载框架

AngularJS 1.x:

Angular JS 1.x

npm

npm

npm install angular

Then add a <script> to your index.html:

然后在您的index.html添加<script>

<script src="/node_modules/angular/angular.js"></script>

bower

凉亭

bower install angular

Then add a <script> to your index.html:

然后在您的index.html添加<script>

<script src="/bower_components/angular/angular.js"></script>

Angular:

角度:

For more information regarding the documentation, refer to the official site of AngularJS.

有关文档的更多信息,请参考AngularJS的官方网站。

You can install Angular 2.x and other versions by following the steps from the official documentation of Angular.

您可以通过以下从官方文档中的步骤安装角2.x和其他版本的角 。

Now let's learn a bit more about Angular, shall we?

现在让我们进一步了解Angular,可以吗?

介绍 (Introduction)

Views offer a necessary layer of abstraction. They keep Angular independent of platform specific utilities. As a cross-platform technology, Angular uses its views to connect with the platform.

视图提供了必要的抽象层。 它们使Angular独立于特定于平台的实用程序。 作为一种跨平台技术,Angular使用其视图来连接平台。

For every element in Angular’s template HTML, there is a corresponding view. Angular recommends interacting with the platforms through these views. While direct manipulation is still possible, Angular warns against it. Angular offers its own application programming interface (API) to replace the native manipulations.

对于Angular模板HTML中的每个元素,都有一个对应的视图。 Angular建议通过这些视图与平台进行交互。 尽管仍然可以进行直接操纵,但Angular警告它。 Angular提供了自己的应用程序编程接口(API)来代替本地操作。

Shunning views for platform-specific API has its consequences. When developing Angular in a web browser, elements exist in two places: the DOM and the view. Messing only with the DOM does not impact the view.

特定于平台的API的回避视图会产生后果。 在Web浏览器中开发Angular时,元素存在于两个位置:DOM和视图。 仅使用DOM不会影响视图。

Since Angular does not interface with the platform, this creates a discontinuity. Views should mirror the platform one-to-one. Otherwise Angular wastes resources managing elements that mismatch it. This is terrible in the event of deleted elements.

由于Angular不与平台交互,因此会造成不连续。 视图应该一对一地反映平台。 否则,Angular会浪费与之不匹配的资源管理元素。 在删除元素的情况下,这很糟糕。

These sorts of discrepancies make views appear unnecessary. Never forget that Angular is a universal development platform above all. Views are a necessary abstraction for this end.

这些差异使视图显得不必要。 永远不要忘记Angular首先是一个通用的开发平台。 为此,视图是必要的抽象。

By adhering to views, Angular applications will function across all supported development platforms. Platforms include the Web, Android, and Apple iOS.

通过遵循视图,Angular应用程序将在所有受支持的开发平台上运行。 平台包括Web,Android和Apple iOS。

注意 (Note)

From here-on, this article assumes a web browser environment. Feel free to mentally replace the DOM with something more applicable to your preferred platform.

从现在开始,本文假设使用Web浏览器环境。 随意用更适合您喜欢的平台的东西替换DOM。

什么是视图? (What are Views?)

Views are almost like their own virtual DOM. Each view contains a reference to a corresponding section of the DOM. Inside a view are nodes that mirror what is in the this section. Angular assigns one view node per DOM element. Each node holds a reference to a matching element.

视图几乎就像它们自己的虚拟DOM。 每个视图都包含对DOM相应部分的引用。 视图内部是反映本节内容的节点。 Angular为每个DOM元素分配一个视图节点。 每个节点都有对匹配元素的引用。

When Angular checks for changes, it checks the views. Angular avoids the DOM under the hood. The views reference the DOM on its behalf. Other mechanisms are in place to ensure that view changes render to the DOM. Conversely, changes to the DOM do not affect the views.

Angular检查更改时,将检查视图。 Angular避免了幕后的DOM。 视图代表DOM引用DOM。 还有其他机制可以确保视图更改呈现给DOM。 相反,对DOM的更改不会影响视图。

Again, views are common across all development platforms besides the DOM. Even if developing for one platform, views are still considered best practice. They guarantee Angular has a correct interpretation of the DOM.

同样,视图在DOM之外的所有开发平台上都是通用的。 即使为一个平台开发,视图仍然被认为是最佳实践。 它们保证Angular对DOM有正确的解释。

Views may not exist on third-party libraries. Direct DOM manipulation is an escape hatch for this kind of scenario. Granted, do not expect the application to function cross-platform.

第三方库上可能不存在视图。 对于这种情况,直接DOM操作是一种逃生方法。 当然,不要期望应用程序可以跨平台运行。

视图类型 (Types of Views)

There are two main types of views: embedded and host.

视图有两种主要类型:嵌入式视图和主机视图。

There also exists view containers. They hold embedded and host views and are often referred to as simple “views”.

还存在视图容器。 它们具有嵌入式和宿主视图,通常称为简单“视图”。

Every @Component class registers a view container (view) with Angular. New components generate a custom selector targeting a certain DOM element. The view attaches to that element wherever it appears. Angular now knows the component exists looking at the view model.

每个@Component类都向Angular注册一个视图容器(视图)。 新组件会生成针对某个DOM元素的自定义选择器。 该视图无论出现在何处都将附加到该元素。 现在,Angular通过查看视图模型知道组件存在。

Host views attach to components created dynamically with factories. Factories provide a blueprint for view instantiation. That way the application can instantiate the component’s host view during runtime. A host view attaches to a component’s wrapper per its instantiation. This view stores data describing conventional component capabilities.

主机视图将附加到使用工厂动态创建的组件。 工厂提供了视图实例化的蓝图。 这样,应用程序可以在运行时实例化组件的主机视图。 主机视图根据其实例化附加到组件的包装器。 此视图存储描述常规组件功能的数据。

<ng-template></ng-template> is a akin to the HTML5 <template></template> element. Angular’s ng-template works with embedded views. These views do not attach to DOM elements unlike host views. They are identical to host views in that they both types exist inside of view containers.

<ng-template></ng-template>类似于HTML5 <template></template>元素。 Angular的ng-template适用于嵌入式视图。 与宿主视图不同,这些视图不会附加到DOM元素。 它们与主机视图相同,因为它们都存在于视图容器内部。

Keep in mind, ng-template is not a DOM element. It gets commented out later leaving nothing but the embedded view nodes behind.

请记住, ng-template不是DOM元素。 稍后将其注释掉,只剩下嵌入式视图节点。

The difference depends on input data; embedded views store no component data. They store a series of elements as nodes comprising its template. The template makes up all the innerHTML of ng-template. Each element within the embedded view is its own separate view node.

差异取决于输入数据。 嵌入式视图不存储任何组件数据。 它们将一系列元素存储为组成其模板的节点。 模板构成了ng-template所有innerHTML。 嵌入式视图中的每个元素都是其自己单独的视图节点。

主机视图和容器 (Host Views and Containers)

Host views host dynamic components. View containers (views) attach automatically to elements already in the template. Views can attach to any element beyond what is unique to component classes.

主机视图承载动态组件。 视图容器(视图)自动附加到模板中已存在的元素。 视图可以附加到任何组件类之外的元素。

Think of the traditional method of component generation. It begins by creating a class, decorating it with @Component, and filling in metadata. This approach occurs for any pre-defined component element of the template.

想想传统的组件生成方法。 首先创建一个类,用@Component装饰它,然后填充元数据。 对于模板的任何预定义组件元素,都会发生这种方法。

Try using the Angular command-line interface (CLI) command: ng generate component [name-of-component]. It yields the following.

尝试使用Angular命令行界面(CLI)命令: ng generate component [name-of-component] 。 它产生以下内容。

import { Component, OnInit } from '@angular/core';@Component({selector: 'app-example',templateUrl: './example.component.html',styleUrls: ['./example.component.css']
})
export class ExampleComponent implements OnInit {constructor() { }ngOnInit() { }
}

This creates the component with the selector app-example. This attaches a view container to <app-example></app-example> in the template. If this were the root of the application, its view would encapsulate all other views. The root view marks the beginning of the application from Angular’s perspective.

这将使用选择器app-example创建组件。 这会将视图容器附加到模板中的<app-example></app-example> 。 如果这是应用程序的根,则其视图将封装所有其他视图。 从Angular的角度来看,根视图标志着应用程序的开始。

Creating components dynamically and registering them in the Angular view model takes a few extra steps. Structural directives help manage dynamic content (*ngIf, *ngFor, and *ngSwitch…). Directives do not scale to bigger applications however. Too many structural directives complicates the template.

动态创建组件并将其注册到Angular视图模型中需要花费一些额外的步骤。 结构化指令有助于管理动态内容( *ngIf, *ngFor, and *ngSwitch… )。 但是,指令无法扩展到更大的应用程序。 太多的结构指令使模板复杂化。

This is where instantiating components from existing class logic comes in handy. These components need to create a host view that can insert into the view model. Host views holds data for components so that Angular recognizes their structural purpose.

这是从现有类逻辑实例化组件的地方。 这些组件需要创建一个可以插入视图模型的宿主视图。 宿主视图保存组件的数据,以便Angular识别其结构目的。

主机视图续 (Host Views Continued)

Every component has a class definition. Yet JavaScript does not support classes. Classes are syntactic sugar. They produce functions containing component factories instead.

每个组件都有一个类定义。 但是JavaScript不支持类。 类是语法糖。 它们产生包含组件工厂的函数。

Factories act as blueprints for host views. They build views to interface with Angular on behalf of their components. Host views attach to DOM elements. Technically any element is OK but the most common target is <ng-component></ng-component>.

工厂充当主机视图的蓝图。 他们构建视图以代表其组件与Angular交互。 主机视图附加到DOM元素。 从技术上讲,任何元素都可以,但最常见的目标是<ng-component></ng-component>

A view container (view) for holding views must first exist. <ng-container></ng-container> is a great place to attach a view container. View containers are the same type of views that also apply to template class elements.

首先必须存在用于保存视图的视图容器(视图)。 <ng-container></ng-container>是附加视图容器的好地方。 视图容器是与视图类型相同的视图,也适用于模板类元素。

A few helpers and references from @angular/core provide the other needed utilities. The following example puts it all together.

@angular/core一些帮助程序和参考提供了其他所需的实用程序。 以下示例将所有内容放在一起。

// another.component.tsimport { Component } from '@angular/core';@Component({template: `<h1>Another Component Content</h1><h3>Dynamically Generated!</h3>`
})
export class AnotherComponent { }
// example.component.tsimport { AfterViewInit, Component, ViewChild,
ViewContainerRef, ComponentFactoryResolver } from '@angular/core';
import { AnotherComponent } from './another.component';@Component({selector: 'app-example',template: `<h1>Application Content</h1><ng-container #container></ng-container><h3>End of Application</h3>`,entryComponents: [ AnotherComponent ]
})
export class ExampleComponent implements AfterViewInit {@ViewChild("container", { read: ViewContainerRef }) ctr: ViewContainerRef;constructor(private resolve: ComponentFactoryResolver) { }ngAfterViewInit() {const factory = this.resolve.resolveComponentFactory(AnotherComponent);this.ctr.createComponent(factory);}
}

Assume AnotherComponent and ExampleComponent are both declared under the same module. AnotherComponent is a simple class component dynamically added into ExampleComponent’s view. ExampleComponent’s entryComponents metadata must contain AnotherComponent for bootstrapping.

假设AnotherComponent和ExampleComponent都在同一模块下声明。 AnotherComponent是一个动态添加到ExampleComponent的视图中的简单类组件。 ExampleComponent的entryComponents元数据必须包含用于引导的 AnotherComponent。

While ExampleComponent is a part of the template, AnotherComponent remains detached. It dynamically renders into the template from ExampleComponent.

虽然ExampleComponent是模板的一部分,但是AnotherComponent仍然是分离的。 它从ExampleComponent动态地渲染到模板中。

There are two view containers present: <app-example></app-example> and <ng-container></ng-container>. The host view for this example will insert into ng-container.

存在两个视图容器: <app-example></app-example><ng-container></ng-container> 。 此示例的宿主视图将插入ng-container

The AfterViewInit lifecycle hook fires after the @ViewChild queries complete. Using the template reference variable #container, the @ViewChild references ng-container as ctr.

@ViewChild查询完成后,将触发AfterViewInit生命周期挂钩。 使用模板引用变量 #container @ViewChild@ViewChildng-container引用为ctr

ViewContainerRef is the type of reference for view containers (views). ViewContainerRef references a view that supports the insertion of other views. ViewContainerRef contains more methods for managing its contained views.

ViewContainerRef是视图容器(视图)的引用类型。 ViewContainerRef引用一个支持其他视图插入的视图。 ViewContainerRef包含更多方法来管理其包含的视图。

Through dependency injection, the constructor instantiates an instance of Angular’s ComponentFactoryResolver service. This service extracts the the factory function (host view blueprint) of AnotherComponent.

通过依赖注入,构造函数实例化Angular的ComponentFactoryResolver服务的实例。 此服务提取AnotherComponent的工厂功能(主机视图蓝图)。

The single argument of createComponent requires a factory. The createComponent function derives from ViewContainerRef. It instantiates AnotherComponent under a host view derived from the component’s factory.

createComponent的单个参数需要一个工厂。 createComponent函数从ViewContainerRef派生。 它在派生自组件工厂的主机视图下实例化AnotherComponent。

The host view then inserts into the view container. <ng-component></ng-component> wraps the component inside of the view container. It has attached to it the aforementioned host view. ng-component is the host view’s connection with the DOM.

然后,宿主视图将插入到视图容器中。 <ng-component></ng-component>将组件包装在视图容器内部。 它已附加上述主机视图。 ng-component是主机视图与DOM的连接。

There are other ways create a host view dynamically from a component. Other ways often focus on optimization.

还有其他方法可以从组件动态创建主机视图。 其他方法通常专注于优化 。

The ViewContainerRef holds a powerful API. It can manage any number of views either host or embedded within its view. The API includes view operations such as insert, move, and delete. This lets you manipulate the DOM through Angular’s view model. This is best practice so that Angular and the DOM match each other.

ViewContainerRef拥有强大的API。 它可以管理宿主视图或嵌入在其视图中的任意数量的视图。 该API包括视图操作,例如插入,移动和删除。 这使您可以通过Angular的视图模型来操作DOM。 最佳做法是使Angular和DOM相互匹配。

嵌入式视图 (Embedded Views)

Note: embedded views attach to other views no added input. Host views attach to a DOM element with input data from its host view describing it as a component.

注意:嵌入式视图附加到其他视图,没有添加输入。 主机视图附加到DOM元素,并带有来自其主机视图的输入数据,将其描述为组件。

Structural directives create an ng-template surrounding a chunk of HTML content. The directive’s host element has a view container attached. This make it so that the content can conditionally render into its intended layout.

结构化指令会在大量HTML内容周围创建一个ng-template 。 指令的host元素具有连接的视图容器。 这样可以使内容可以有条件地呈现为其预期的布局。

The ng-template holds embedded view nodes representing each element within its innerHTML. ng-template is by no means a DOM element. It comments itself out. The tags define the extend of its embedded view.

ng-template包含表示其innerHTML中每个元素的嵌入式视图节点。 ng-template绝不是DOM元素。 它自行注释掉。 标签定义其嵌入式视图的扩展。

嵌入式视图续 (Embedded Views Continued)

Instantiating an embedded view requires no external resources beyond its own reference. The @ViewChild query can fetch that.

实例化嵌入式视图不需要其引用之外的任何外部资源。 @ViewChild查询可以获取该信息。

With the template reference, calling createEmbeddedView from it does the trick. The innerHTML of the reference becomes its own embedded view instance.

使用模板引用,从中调用createEmbeddedView可以解决问题。 引用的innerHTML成为其自己的嵌入式视图实例。

In the next example, <ng-container></ng-container> is a view container. ng-container gets commented out during compilation just like ng-template. Thus it provides an outlet for inserting the embedded view while keeping the DOM lean.

在下一个示例中, <ng-container></ng-container>是一个视图容器。 ng-container就像ng-template一样在编译过程ng-container被注释掉。 因此,它提供了用于在保持DOM精简的同时插入嵌入式视图的出口。

The embedded view template inserts at the layout location of ng-container. This newly inserted view has no additional view encapsulation besides the view container. Remember how that differs from host views (host views attach to their ng-component element wrapper).

嵌入式视图模板将插入ng-container的布局位置。 这个新插入的视图除视图容器外没有其他视图封装。 记住与主机视图有何不同(主机视图附加到其ng-component元素包装器)。

import { Component, AfterViewInit, ViewChild,
ViewContainerRef, TemplateRef } from '@angular/core';@Component({selector: 'app-example',template: `<h1>Application Content</h1><ng-container #container></ng-container> <!-- embed view here --><h3>End of Application</h3><ng-template #template><h1>Template Content</h1><h3>Dynamically Generated!</h3></ng-template>`
})
export class ExampleComponent implements AfterViewInit {@ViewChild("template", { read: TemplateRef }) tpl: TemplateRef<any>;@ViewChild("container", { read: ViewContainerRef }) ctr: ViewContainerRef;constructor() { }ngAfterViewInit() {const view =  this.tpl.createEmbeddedView(null);this.ctr.insert(view);}
}

@ViewChild queries for the template reference variable #template. This provides a template reference of type TemplateRef. TemplateRef holds the createEmbeddedView function. It instantiates the template as an embedded view.

@ViewChild查询模板引用变量 #template 。 这提供类型的模板参考TemplateRefTemplateRef拥有createEmbeddedView函数。 它将模板实例化为嵌入式视图。

The single argument of createEmbeddedView is for context. If you wanted to pass in additional metadata, you could do it here as an object. The fields should match up with the ng-template attributes (let-[context-field-key-name]=“value”). Passing null indicates no extra metadata is necessary.

createEmbeddedView的单个参数用于上下文。 如果您想传递其他元数据,则可以在此处作为对象来进行传递。 这些字段应与ng-template属性匹配( let-[context-field-key-name]=“value” )。 传递null表示不需要额外的元数据。

A second @ViewChild query provides a reference to ng-container as a ViewContainerRef. Embedded views only attach to other views, never the DOM. The ViewContainerRef references the view that takes in the embedded view.

第二个@ViewChild查询提供对ng-container的引用作为ViewContainerRef 。 嵌入式视图仅附加到其他视图,而不附加到DOM。 ViewContainerRef引用嵌入视图中的视图。

An embedded view may also insert into the component view of <app-example></app-example>. This approach positions the view at the very end of ExampleComponent’s view. In this example however, we want the content to show up in the very middle where ng-container sits.

嵌入式视图也可以插入<app-example></app-example>的组件视图中。 这种方法将视图放置在ExampleComponent视图的最末端。 但是,在此示例中,我们希望内容显示在ng-container所在的中间位置。

The ViewContainerRef insert function inserts the embedded view into the ng-container. The view content shows ups in the intended location right in the middle of ExampleComponent’s view.

ViewContainerRef insert函数嵌入的视图插入ng-container 。 视图内容将显示在ExampleComponent视图中间的预期位置。

结论 (Conclusion)

Manipulating the DOM with platform specific methods is not recommended. Creating and managing a tight set of views keeps Angular and the DOM on the same page. Updating the views informs Angular of the current state of the DOM. Updates to the views also carry over into what the DOM displays.

不建议使用平台特定的方法来操作DOM。 创建和管理一组紧密的视图会使Angular和DOM保持在同一页面上。 更新视图会通知Angular DOM当前状态。 对视图的更新也会保留到DOM显示的内容中。

Angular provides a flexible API for view interaction. Developing platform independent applications is possible thanks to this level of abstraction. Of course, the temptation to fallback on platform dependent strategies persists. Unless you have a very good reason not to, try to stick with the views API Angular provides. This will yield predictable results across all platforms.

Angular为视图交互提供了一个灵活的API。 由于这种抽象水平,开发与平台无关的应用程序成为可能。 当然,回退依赖于平台的策略的诱惑仍然存在。 除非您有很好的理由不这样做,否则请尝试使用API​​ Angular提供的视图。 这将在所有平台上产生可预测的结果。

角度布线 (Routing in Angular)

Routing is essential. Many modern web applications host too much information for one page. Users should not have to scroll through an entire application’s worth of content either. An application needs to split itself into distinguishable sections.

路由至关重要。 许多现代的Web应用程序为一页承载太多信息。 用户也不必滚动浏览整个应用程序的内容。 应用程序需要将自身拆分为不同的部分。

Users prioritize necessary information. Routing helps them find the application section with such information. Any other information useful to other users may exist on an entirely separate route. With routing, both users can find what they need quickly. Irrelevant details stay obscured behind irrelevant routes.

用户优先考虑必要的信息。 路由帮助他们找到带有此类信息的应用程序部分。 对其他用户有用的任何其他信息都可以存在于完全独立的路径上。 通过路由,两个用户都可以快速找到他们需要的东西。 不相关的细节在不相关的路线后面被遮盖。

Routing excels at sorting and restricting access to application data. Sensitive data should never display to unauthorized users. Between every route the application may intervene. It can examine a user’s session for authentication purposes. This examination determines what the route renders if it should render at all. Routing gives developers the perfect chance to verify a user before proceeding.

路由擅长于排序和限制对应用程序数据的访问。 敏感数据绝不能显示给未经授权的用户。 在每条路线之间,应用程序都可能会介入。 它可以检查用户的会话以进行身份​​验证。 该检查确定路线是否应该渲染。 路由为开发人员提供了在继续操作之前验证用户的绝佳机会。

Creating a list of routes promotes organization as well. In terms of development, it keeps the developer thinking in distinguishable sections. Users benefit from this too, but more-so developers when navigating the application code. A list of programmatic routers paints an accurate model of the application’s front end.

创建路由列表也可以促进组织。 在开发方面,它使开发人员可以在可区分的部分中进行思考。 用户也从中受益,但是开发人员在浏览应用程序代码时也会从中受益。 一系列编程路由器描绘了应用程序前端的准确模型。

As for Angular, routing takes up its own entire library within the framework. All modern front-end frameworks support routing, and Angular is no different. Routing happens from the client-side using either hash or location routing. Both styles allow the client to manage its own routes. No additional assistance from the server is necessary past the initial request.

至于Angular,路由在框架内占用了它自己的整个库。 所有现代的前端框架都支持路由,而Angular也不例外。 使用散列或位置路由从客户端进行路由。 两种样式都允许客户端管理自己的路由。 在初始请求之后,无需服务器的其他帮助。

The web browser rarely refreshes using client-side routing. Web browser utilities such as bookmarks, history, and the address bar still work despite no refreshing. This makes for a slick routing experience that does not mess up the browser. No more jumpy page reloads while routing to a different page.

Web浏览器很少使用客户端路由刷新。 尽管没有刷新,诸如书签,历史记录和地址栏之类的Web浏览器实用程序仍然可以使用。 这提供了流畅的路由体验,不会干扰浏览器。 路由到其他页面时,不再需要重新加载页面。

Angular adds on a layer of abstraction over the core technologies used for routing. This article intends to explain this abstraction. There exists two routing strategies in Angular: path location and hash. This article focuses on the path location strategy since its the default option.

Angular在用于路由的核心技术上增加了一层抽象。 本文旨在解释这种抽象。 Angular中存在两种路由策略:路径位置和哈希。 本文重点介绍路径定位策略,因为它是默认选项。

Plus, path location may deprecate hash routing following the full release of Angular Universal. Regardless, the two strategies are very similar in implementation. Learning one learns the other. Time to get started!

另外,在Angular Universal全面发布之后,路径位置可能会弃用哈希路由。 无论如何,这两种策略在实现上非常相似。 学习一个学习另一个。 是时候开始了!

路由器模块设置 (RouterModule Setup)

Routing utilities export with RouterModule available from @angular/router. It is not part of the core library since not all applications require routing. The most conventional way to introduce routing is as its own feature module.

路由实用程序可通过@angular/router RouterModule导出。 它不是核心库的一部分,因为并非所有应用程序都需要路由。 引入路由的最传统方法是作为其自身的功能模块 。

As route complexity grows, having it as its own module will promote the root module’s simplicity. Keeping it stupid simple without compromising functionality constitutes good design for modules.

随着路由复杂度的增加,将其作为自己的模块将促进根模块的简单性。 在不影响功能的情况下保持愚蠢的简单性构成了模块的良好设计。

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';import { AComponent } from '../../components/a/a.component';
import { BComponent } from '../../components/b/b.component';// an array of soon-to-be routes!
const routes: Routes = [];@NgModule({imports: [ RouterModule.forRoot(routes) ],exports: [ RouterModule ]
})
export class AppRoutingModule { }

.forRoot(...) is a class function available from the RouterModule class. The function accepts an array of Route objects as Routes. .forRoot(...) configures routes for eager-loading while its alternative .forChild(...) configures for lazy-loading.

.forRoot(...)是RouterModule类中可用的类函数。 该函数接受Route对象数组作为Routes.forRoot(...)配置路由以进行.forChild(...)加载,而其替代.forChild(...)配置为延迟加载。

Eager-loading meaning the routes load their content into the application from the get-go. Lazy-loading happens on-demand. The focus of this article is eager-loading. It is the default approach for loading in an application. The RouterModule class definition looks something like the next block of code.

急切加载意味着路由从一开始就将其内容加载到应用程序中。 延迟加载按需进行。 本文的重点是急切加载。 这是在应用程序中加载的默认方法。 RouterModule类定义类似于下一个代码块。

@NgModule({// … lots of metadata ...
})
export class RouterModule {forRoot(routes: Routes) {// … configuration for eagerly loaded routes …}forChild(routes: Routes) {// … configuration for lazily loaded routes …}
}

Do not worry about the configuration details the example omits with comments. Having a general understanding will do for now.

不必担心示例中带有注释的配置详细信息。 大致了解现在就可以了。

Notice how AppRoutingModule imports the RouterModule while also exporting it. This makes sense given AppRoutingModule is a feature module. It imports into the root module as a feature module. It exposes RouterModule directives, interfaces, and services to the root component tree.

请注意AppRoutingModule如何导入RouterModule并同时将其导出。 鉴于AppRoutingModule是功能模块,这是有道理的。 它将作为功能模块导入到根模块中。 它将RouterModule指令,接口和服务公开给根组件树。

This explains why AppRoutingModule must export RouterModule. It does so for the sake of the root module’s underlying component tree. It needs access to those routing utilities!

这解释了为什么AppRoutingModule必须导出RouterModule。 这样做是为了根模块的基础组件树。 它需要访问那些路由实用程序!

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';import { AppComponent } from './app.component';
import { AComponent } from './components/a/a.component';
import { BComponent } from './components/b/b.component';
import { AppRoutingModule } from './modules/app-routing/app-routing.module';@NgModule({declarations: [AppComponent,AComponent,BComponent],imports: [AppRoutingModule, // routing feature moduleBrowserModule],providers: [],bootstrap: [ AppComponent ]
})
export class AppModule { }

The AppRoutingModule token imports from the very top. Its token inserts into the root module’s imports array. The root component tree may now utilize the RouterModule library. That includes its directives, interfaces, and services as already mentioned. Big thanks goes to AppRoutingModule for exporting RouterModule!

AppRoutingModule令牌从最顶部导入。 它的令牌插入到根模块的imports数组中。 根组件树现在可以利用RouterModule库。 如前所述,这包括其指令,接口和服务。 非常感谢AppRoutingModule导出RouterModule!

The RouterModule utilities will come in handy for the root’s components. The basic HTML for AppComponent makes use of one directive: router-outlet.

RouterModule实用程序将对根目录的组件派上用场。 AppComponent的基本HTML使用一个指令: router-outlet

<!-- app.component.html --><ul><!-- routerLink(s) here -->
</ul>
<router-outlet></router-outlet>
<!-- routed content appends here (AFTER THE ELEMENT, NOT IN IT!) -->

routerLink is an attribute directive of RouterModule. It will attach to each element of <ul></ul> once the routes are setup. router-outlet is a component directive with interesting behavior. It acts more as a marker for displaying routed content. Routed content results from navigation to a specific route. Usually that means a single component as configured in AppRoutingModule

routerLinkrouterLink的属性指令。 设置路由后,它将附加到<ul></ul>每个元素。 router-outlet是具有有趣行为的组件指令。 它更多地用作显示路由内容的标记。 路由内容是从导航到特定路由的结果。 通常,这意味着在AppRoutingModule中配置的单个组件

The routed content renders right after <router-outlet></router-outlet>. Nothing renders inside of it. This does not make too much of a considerable difference. That said, do not expect router-outlet to behave like a container for routed content. It is merely a marker for appending routed content to the Document Object Model (DOM).

路由的内容在<router-outlet></router-outlet>之后呈现。 里面什么也没有。 这并没有太大的区别。 就是说,不要指望router-outlet表现得像是路由内容的容器。 它只是用于将路由内容附加到文档对象模型(DOM)的标记。

基本路由 (Basic Routing)

The previous section establishes the basic setup for routing. Before actual routing can happen, a few more things must be addressed

上一节建立了路由的基本设置。 在实际的路由发生之前,还必须解决一些其他问题

The first question to address is what routes will this application consume? Well, there are two components: AComponent and BComponent. Each one should have its own route. They can render from AppComponent’s router-outlet depending on the current route location.

要解决的第一个问题是此应用程序将消耗哪些路由? 好,有两个组件:AComponent和BComponent。 每个人都应该有自己的路线。 它们可以根据当前路线位置从AppComponent的router-outlet进行渲染。

The route location (or path) defines what appends to a website’s origin (e.g. http://localhost:4200) through a series of slashes (/).

路由位置(或路径)定义了通过一系列斜杠( / )附加到网站来源 (例如http:// localhost:4200 )的内容。

// … same imports from before …const routes: Routes = [{path: 'A',component: AComponent},{path: 'B',component: BComponent}
];@NgModule({imports: [ RouterModule.forRoot(routes) ],exports: [ RouterModule ]
})
export class AppRoutingModule { }

http://localhost:4200/A renders AComponent from AppComponent’s router-outlet. http://localhost:4200/B renders BComponent. You need a way to route to these locations without using the address bar though. An application should not rely upon a web browser’s address bar for navigation.

http://localhost:4200/A从AppComponent的router-outlet out呈现AComponent。 http://localhost:4200/B呈现BComponent。 您需要一种无需使用地址栏即可路由到这些位置的方法。 应用程序不应依赖Web浏览器的地址栏进行导航。

The global CSS (Cascading Style-sheets) supplements the HTML below it. An application’s router link ought to have a pleasant appearance. This CSS applies to all other examples too.

全局CSS(层叠样式表)补充了其下方HTML。 应用程序的路由器链接应具有愉悦的外观。 该CSS也适用于所有其他示例。

/* global styles.css */ul li {cursor: pointer;display: inline-block;padding: 20px;margin: 5px;background-color: whitesmoke;border-radius: 5px;border: 1px solid black;
}ul li:hover {background-color: lightgrey;
}
<!-- app.component.html --><ul><li routerLink="/A">Go to A!</li><li routerLink="/B">Go to B!</li>
</ul>
<router-outlet></router-outlet>

This is basic routing! Clicking either of the routerLink elments routes the web address. It reassigns it without refreshing the web browser. Angular’s Router maps the routed address to the Routes configured in AppRoutingModule. It matches the address to the path property of a single Route object within the array. First match always wins, so match-all routes should lie at the very end of the Routes array.

这是基本的路由! 单击任一routerLink元素将路由该网址。 它会重新分配它,而不刷新Web浏览器。 Angular的Router将路由的地址映射到AppRoutingModule中配置的Routes 。 它将地址与数组中单个Route对象的path属性匹配。 首次比赛总是获胜,因此所有比赛路线都应位于Routes数组的末尾。

Match-all routes prevent the application from crashing if it cannot match the current route. This can happen from the address bar where the user may type in any route. For this, Angular provides a wildcard path value ** that accepts all routes. This route usually renders a PageNotFoundComponent component displaying “Error 404: Page not found”.

全部匹配路由可以防止应用程序如果无法匹配当前路由而崩溃。 这可以从用户可以在其中输入任何路线的地址栏中发生。 为此,Angular提供了一个通配符路径值** ,该值可以接受所有路由。 此路由通常会渲染一个显示“错误404:找不到页面”的PageNotFoundComponent组件。

// … PageNotFoundComponent imported along with everything else …const routes: Routes = [{path: 'A',component: AComponent},{path: 'B',component: BComponent},{path: '',redirectTo: 'A',pathMatch: 'full'},{path: '**',component: PageNotFoundComponent}
];

The Route object containing redirectTo keeps the PageNotFoundComponent from rendering as a result of http://localhost:4200. This is the applications home route. To fix this, redirectTo reroutes the home route to http://localhost:4200/A. http://localhost:4200/A indirectly becomes the application’s new home route.

包含redirectToRoute对象使PageNotFoundComponent不会由于http://localhost:4200而呈现。 这是应用程序的本地路由。 要解决此问题, redirectTo将本地路由重新路由到http://localhost:4200/A http://localhost:4200/A间接成为应用程序的新本地路由。

The pathMatch: 'full' tells the Route object to match against the home route (http://localhost:4200). It matches the empty path.

pathMatch: 'full'告诉Route对象与本地路由( http://localhost:4200 )匹配。 它匹配空路径。

These two new Route objects go at the end of the array since first match wins. The last array element (path: '**') always matches, so it goes last.

由于首场比赛获胜,这两个新的Route对象位于数组的末尾。 最后一个数组元素( path: '**' )始终匹配,因此位于最后。

There is one last thing worth addressing before moving on. How does the user know where he or she is in the application relative to the current route? Sure there may be content specific to the route, but how is user supposed to make that connection? There should be some form of highlighting applied to the routerLinks. That way, the user will know which route is active for the given web page.

在继续之前,还有最后一件事值得解决。 用户如何知道他或她在应用程序中相对于当前路线的位置? 当然,可能存在特定于该路线的内容,但是用户应该如何建立该连接? 应该对路由器链接应用某种形式的突出显示。 这样,用户将知道哪个路由对于给定的网页是活动的。

This is an easy fix. When you click a routerLink element, Angular’s Router assigns focus to it. This focus can trigger certain styles which provide useful feedback to the user. The routerLinkActive directive can track this focus for the developer.

这是一个简单的修复。 当您单击routerLink元素时,Angular的Router会为其分配焦点 。 该焦点可以触发某些样式,这些样式可以为用户提供有用的反馈。 routerLinkActive指令可以为开发人员跟踪此焦点。

<!-- app.component.html --><ul><li routerLink="/A" routerLinkActive="active">Go to A!</li><li routerLink="/B" routerLinkActive="active">Go to B!</li>
</ul>
<router-outlet></router-outlet>

The right assignment of routerLinkActive represents a string of classes. This example portrays only one class (.active), but any number of space-delimited classes may apply. When the Router assigns focus to a routerLink, the space-delimited classes apply to the host element. When the focus shifts away, the classes get removed automatically.

正确的routerLinkActive分配表示一串类。 本示例仅描绘一个类( .active ),但是可以应用任何数量的以空格分隔的类。 当Router焦点分配给routerLink时,以空格分隔的类适用于主机元素。 当焦点移开时,这些类将自动删除。

/* global styles.css */.active {background-color: lightgrey !important;
}

Users can now easily recognize how the current route and the page content coincide. lightgrey highlighting applies to the routerLink matching the current route. !important ensures the highlighting overrides inline stylings.

用户现在可以轻松地识别当前路线和页面内容的重合方式。 lightgrey高亮适用于routerLink匹配当前的路线。 !important确保突出显示覆盖内联样式。

参数化路线 (Parameterized Routes)

Routes do not have to be completely hard-coded. They can contain dynamic variables referenceable from the component corresponding the Route object. These variables are declared as parameters when writing the route’s path.

路由不必完全硬编码。 它们可以包含可从对应于Route对象的组件引用的动态变量。 这些变量在写入路径时被声明为参数。

Route parameters are either optional or mandatory for matching a particular Route. It depends on how a route writes its parameters. Two strategies exist: matrix and traditional parameterization.

路由参数对于匹配特定Route是可选的或必需的。 这取决于路由如何写入其参数。 存在两种策略:矩阵和传统参数化。

Traditional parameterization begins from the Routes array configured in AppRoutingModule.

传统的参数化从AppRoutingModule中配置的Routes数组开始。

const routes: Routes = [// … other routes …{path: 'B',component: BComponent},{path: 'B/:parameter',component: BComponent},// … other routes …
];

Focus on the two BComponent routes. Parameterization will eventually occur in both routes.

关注两条BComponent路线。 参数化最终将在两条路径中发生。

Traditional parameterization occurs in the second BComponent Route. B/:parameter contains the parameter parameter as indicated with the :. Whatever follows the colon marks the parameter’s name. The parameter parameter is necessary for the second BComponent Route to match.

传统的参数化发生在第二个BComponent RouteB/:parameter包含parameter参数,如: 。 冒号后面的所有内容都会标记参数的名称。 parameter parameter是第二个BComponent Route匹配所必需的。

parameter reads in the value of whatever gets passed into the route. Routing to http://localhost:4200/B/randomValue will assign parameter the value of randomValue. This value can include anything besides another /. For example, http://localhost:4200/B/randomValue/blahBlah will not trigger the second BComponent Route. The PageNotFoundComponent renders instead.

parameter读取传递到路由中的值。 路由到http://localhost:4200/B/randomValue将分配parameter的值randomValue 。 该值可以包含/以外的任何值。 例如, http://localhost:4200/B/randomValue/blahBlah将不会触发第二个BComponent Route 。 而不是PageNotFoundComponent呈现。

BComponent can reference route parameters from its component class. Both approaches to parameterization (matrix and traditional) yield the same results in BComponent. Before seeing BComponent, examine the matrix form of parameterization below.

BComponent可以从其组件类中引用路由参数。 两种参数化方法(矩阵和传统方法)在BComponent中产生相同的结果。 在看到BComponent之前,请检查下面的参数化矩阵形式。

// app.component.tsimport { Component } from '@angular/core';
import { Router } from '@angular/router';@Component({selector: 'app-root',templateUrl: './app.component.html'
})
export class AppComponent {constructor(private router: Router) { }routeMatrixParam(value: string) {if (value)this.router.navigate(['B', { parameter: value }]); // matrix parameterelsethis.router.navigate(['B']);}routeAddressParam(value: string) {this.router.navigate(['B', value]);}
}

Angular’s dependency injection system provides an instantiation of the Router. This lets the component programmatically route. The .navigate(...) function accepts an array of values that resolves to a routable path. Something like .navigate(['path', 'to', 'something']) resolves to http://localhost:4200/path/to/something. .navigate(...) adds path-delimiting / marks when normalizing the array into a routable path.

Angular的依赖项注入系统提供Router的实例化。 这使组件可以编程方式进行布线。 .navigate(...)函数接受一个解析为可路由路径的值数组。 诸如.navigate(['path', 'to', 'something'])解析为http://localhost:4200/path/to/something.navigate(...)在将数组归一化为可路由路径时添加了路径.navigate(...) /标记。

The second form of parameterization occurs in routeMatrixParam(...). See this line of code: this.router.navigate(['B', { parameter: value }]). This form of parameter is a matrix parameter. Its value is optional for the first BComponent Route to match (/B). The Route matches regardless of the parameter’s presence in the path.

参数化的第二种形式出现在routeMatrixParam(...) 。 请参见以下代码行: this.router.navigate(['B', { parameter: value }]) 。 这种形式的parameter是矩阵参数。 对于第一个匹配的BComponent Route ( /B ),它的值是可选的。 该Route无论在路径参数的存在相匹配。

The routeAddressParam(...) resolves a route that matches the http://localhost:4200/B/randomValue parameterization approach. This traditional strategy needs a parameter to match the second BComponent route (B/:parameter).

routeAddressParam(...)解析与http://localhost:4200/B/randomValue参数化方法匹配的路由。 这种传统策略需要一个参数来匹配第二个BComponent路由( B/:parameter )。

The matrix strategy concerns routeMatrixParam(...). With or without a matrix parameter in its path, the first BComponent route still matches. The parameter parameter passes to BComponent just like with the traditional approach.

矩阵策略涉及routeMatrixParam(...) 。 路径中有或没有矩阵参数,第一个BComponent路由仍然匹配。 parameter参数传递给BComponent就像传统方法一样。

To make full sense of the above code, here is the corresponding template HTML.

为了充分理解上述代码,以下是相应的模板HTML。

// app.component.html<ul><li routerLink="/A">Go to A!</li><li><input #matrixInput><button (click)="routeMatrixParam(matrixInput.value)">Matrix!</button></li><li><input #paramInput><button (click)="routeAddressParam(paramInput.value)">Param!</button></li>
</ul>
<router-outlet></router-outlet>

In the template, values are accepted as text input. The input injects it into the route path as a parameter. Two separate sets of boxes exist for each parameterization strategy (traditional and matrix). With all the pieces coming together, it is time to examine the BComponent component class.

在模板中,值被接受为文本输入。 输入将其作为参数注入到路径中。 每个参数化策略(传统方法和矩阵方法)都有两组独立的框。 将所有部分放在一起,是时候检查BComponent组件类了。

// b.component.tsimport { Component, OnInit } from '@angular/core';
import { ActivatedRoute, ParamMap } from '@angular/router';@Component({selector: 'app-b',template: `<p>Route param: {{ currParam }}</p>`
})
export class BComponent implements OnInit {currParam: string = "";constructor(private route: ActivatedRoute) { }ngOnInit() {this.route.params.subscribe((param: ParamMap) => {this.currParam = param['parameter'];});}
}

BComponent results from either of two BComponent routes in AppRoutingModule. ActivatedRoute instantiates into a set of useful information pertaining to the current route. That is, the route that caused BComponent to render. ActivatedRoute instantiates via dependency injection targeting the class constructor.

BComponent来自AppRoutingModule中的两个BComponent路由之一。 ActivatedRoute实例化为一组与当前路由有关的有用信息。 也就是说,导致BComponent渲染的路由。 ActivatedRoute通过针对类构造函数的依赖注入实例化。

The .params field of ActivatedRoute.params returns an Observable which emits the route parameters. Notice how the two different parameterization approaches result in the parameter parameter. The returned Observable emits it as a key-value pair inside of a ParamMap object.

ActivatedRoute.params.params字段返回一个Observable ,它发出路由参数。 请注意,两种不同的参数化方法是如何产生parameter参的。 返回的Observable其作为ParamMap对象内部的键值对发出。

Between the two parameterization approaches, the parameter parameter resolved identically. The value emits from ActivatedRoute.params despite the approach to parameterization.

在两种参数化方法之间, parameter参数解析相同。 尽管采用了参数化方法,但该值仍从ActivatedRoute.params发出。

The address bar distinguishes the final results of each approach. Matrix parameterization (optional for Route match) yields the address: http://localhost:4200/B;parameter=randomValue. Traditional parameterization (required for Route match) yields: http://localhost:4200/B/randomValue.

地址栏区分每种方法的最终结果。 矩阵参数化(对于Route match是可选的)产生地址: http://localhost:4200/B;parameter=randomValue 。 传统的参数化(对于Route匹配是必需的)产生: http://localhost:4200/B/randomValue

Either way, the same BComponent results. The actual difference: a different BComponent Route matches. This entirely depends upon the parameterization strategy. The matrix approach ensures parameters are optional for Route matching. The traditional approach requires them.

无论哪种方式,都会得到相同的BComponent。 实际的区别:不同的BComponent Route匹配。 这完全取决于参数化策略。 矩阵方法确保参数对于Route匹配是可选的。 传统方法需要它们。

嵌套路线 (Nested Routes)

Routes may form a hierarchy. In the DOM, this involves one parent router-outlet rendering at least one child router-outlet. In the address bar, it looks like this: http://localhost/parentRoutes/childRoutes. In the Routes configuration, the children: [] property denotes a Route object as having nested (child) routes.

Routes可以形成层次结构。 在DOM中,这涉及一个父router-outlet至少渲染一个子router-outlet 。 在地址栏中,它看起来像这样: http://localhost/parentRoutes/childRoutes 。 在Routes配置中, children: []属性将Route对象表示为具有嵌套(子)路由。

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';import { NestComponent } from '../../components/nest/nest.component';
import { AComponent } from '../../components/nest/a/a.component';
import { BComponent } from '../../components/nest/b/b.component';const routes: Routes = [{path: 'nest',component: NestComponent,children: [{ path: 'A', component: AComponent },{ path: 'B', component: BComponent }]}
];@NgModule({imports: [ RouterModule.forRoot(routes) ],exports: [ RouterModule ]
})
export class AppRoutingModule { }
// nest.component.tsimport { Component } from '@angular/core';@Component({selector: 'app-nest',template: `<ul><li routerLink="./A">Go to A!</li><li routerLink="./B">Go to B!</li></ul><router-outlet></router-outlet>`
})
export class NestComponent { }

NestComponent renders a router-outlet after rendering itself from another root-level router-outlet in AppComponent. The router-outlet of NestComponent’s template may render either AComponent (/nest/A) or BComponent (/nest/B).

NestComponent呈现一个router-outlet从另一个根级渲染本身后router-outlet在AppComponent。 NestComponent模板的router-outlet可以呈现AComponent( /nest/A )或BComponent( /nest/B )。

The AppRoutingModule reflects this nesting in NestComponent’s Route object. The children: [] field holds an array of Route objects. These Route object may also nest routes in their children: [] fields. This can continue for however many layers of nested routes. The above example shows two layers of nesting.

AppRoutingModule在NestComponent的Route对象中反映了此嵌套。 children: []字段包含Route对象的数组。 这些Route对象还可以将路由嵌套在其children: []字段中。 但是,对于多层嵌套路由,这可以继续进行。 上面的示例显示了两层嵌套。

Each routerLink contains a ./ as compared to /. The . ensures that the routerLink appends to the route path. The routerLink completely replaces the path otherwise. After routing to /nest, . expands into /nest.

/相比,每个routerLink包含一个./ 。 的. 确保routerLink附加到路由路径。 否则,routerLink会完全替换路径。 路由到/nest. 扩展为/nest

This is useful for routing to either /nest/A or /nest/B from the .nest route. A and B constitute nested routes of /nest. Routing to /A or /B returns PageNotFound. /nest must prepend the two routes.

这对于从.nest路由到/nest/A/nest/B .nestAB构成/nest嵌套路由。 路由到/A/B返回PageNotFound。 /nest必须在两条路由之前。

Take a look at the AppComponent containing the root-level router-outlet in its template. AppComponent is the first layer of nesting while NestComponent is the second.

看一下在其模板中包含根级router-outlet的AppComponent。 AppComponent是嵌套的第一层,而NestComponent是第二层。

import { Component } from '@angular/core';@Component({selector: 'app-root',template: `<ul><li routerLink="/nest">Go to nested routes!</li><li routerLink="/">Back out of the nested routes!</li></ul><router-outlet></router-outlet>`
})
export class AppComponent { }

Inside the nest Route object, the children: [] contains two more nested routes. They result in AComponent and BComponent when routing from /nest as previously discussed. These components are very simple for the sake of demonstration. <li routerLink="/">...</li> lets you navigate out of the nest routes to reset the example by navigating to the home route.

在嵌套Route对象内部, children: []包含另外两个嵌套路由。 如前所述,从/nest路由时,它们将导致AComponent和BComponent。 为了演示,这些组件非常简单。 <li routerLink="/">...</li>可让您从嵌套路由中导航出来,以导航至本地路由来重置示例。

import { Component } from '@angular/core';@Component({selector: 'app-a',template: `<p>a works!</p>`
})
export class AComponent { }
import { Component } from '@angular/core';@Component({selector: 'app-b',template: `<p>b works!</p>`
})
export class BComponent { }

The children: [] array accepts Route object as elements. children: [] can apply to any of these elements as well. The children of these elements can continue nesting. This pattern may continue for however many layers of nesting. Insert a router-outlet into the template for every layer of nested routing.

children: []数组接受Route对象作为元素。 children: []也可以应用于这些元素中的任何一个。 这些元素的子级可以继续嵌套。 无论多层嵌套如何,这种模式都可以继续。 将嵌套路由的每一层的路由router-outlet插入模板。

Routing techniques apply regardless of a Route object’s level of nesting. The parameterization techniques differ in only one aspect. Child routes can only access their parent’s parameters via ActivatedRoute.parent.params. ActivatedRoute.params targets the same level of nested routes. This excludes parent-level routes and their parameters.

无论Route对象的嵌套级别如何,都适用路由技术。 参数化技术仅在一方面不同。 子路由只能通过ActivatedRoute.parent.params访问其父级的参数。 ActivatedRoute.params目标是相同级别的嵌套路由。 这不包括父级路由及其参数。

Route guards are especially suited for nested routing. One Route object can restrict access to all its nested (child) routes.

Route防护器特别适合嵌套路由。 一个Route对象可以限制对其所有嵌套(子)路由的访问。

守卫路线 (Guarded Routes)

Web applications often consist of public and private data. Both types of data tend to have their own pages with guarded routes. These routes allow/restrict access depending on the user’s privileges. Unauthorized users may interact with a guarded route. The route should block the user if he or she attempts to access its routed content.

Web应用程序通常包含公共和私有数据。 两种类型的数据都有自己的带有受保护路由的页面。 这些路由允许/限制访问,具体取决于用户的权限。 未经授权的用户可以与受保护的路由进行交互。 如果用户尝试访问其路由内容,则该路由应阻止用户。

Angular provides a bundle of authentication guards that can attach to any route. These methods trigger automatically depending on how the user interacts with the guarded route.

Angular提供了可以连接到任何路由的身份验证防护包。 这些方法将根据用户与受保护路线的交互方式自动触发。

  • canActivate(...) - fires when the user attempts to access a route

    canActivate(...) -用户尝试访问路线时触发

  • canActivateChild(...) - fires when the user attempts to access a route’s nested (child) routes

    canActivateChild(...) -当用户尝试访问路由的嵌套(子)路由时触发

  • canDeactivate(...) - fires when the user attempts to leave a route

    canDeactivate(...) -用户尝试离开路线时触发

Angular’s guard methods are available from @angular/router. To help them authenticate, they may optionally receive a few parameters. Such parameters do not inject via dependency injection. Under the hood, each value gets passed in as an argument to the invoked guard method.

可以从@angular/router获得Angular的保护方法。 为了帮助他们进行身份验证,他们可以选择接收一些参数。 此类参数不会通过依赖项注入来注入。 在幕后,每个值都作为参数传递给调用的guard方法。

  • ActivatedRouteSnapshot - available to all three

    ActivatedRouteSnapshot全部三个都可用

  • RouterStateSnapshot - available to all three

    RouterStateSnapshot适用于所有三个

  • Component - available to canDeactivate(...)

    Component -可用于canDeactivate(...)

ActivatedRouteSnapshot provides access to the route parameters of the guarded route. RouterStateSnapshot exposes the URL (uniform resource locator) web address matching the route. Component references the component rendered by the route.

ActivatedRouteSnapshot提供对受保护路由的路由参数的访问。 RouterStateSnapshot公开与路由匹配的URL(统一资源定位符)网址。 Component引用路线所呈现的组件。

To guard a route, a class implementing the guard methods needs to first exist as a service. The service can inject into AppRoutingModule to guard its Routes. The token value for the service may inject into any one Route object.

要保护路由,必须首先将实现保护方法的类作为服务存在。 该服务可以注入AppRoutingModule中以保护其Routes 。 服务的令牌值可以注入到任何一个Route对象中。

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';import { AuthService } from '../../services/auth.service';
import { UserService } from '../../services/user.service';import { PrivateNestComponent } from '../../components/private-nest/private-nest.component';
import { PrivateAComponent } from '../../components/private-nest/private-a/private-a.component';
import { PrivateBComponent } from '../../components/private-nest/private-b/private-b.component';const routes: Routes = [{path: 'private-nest',component: PrivateNestComponent,canActivate: [ AuthService ], // !!!canActivateChild: [ AuthService ], // !!!canDeactivate: [ AuthService ], // !!!children: [{ path: 'private-A', component: PrivateAComponent },{ path: 'private-B', component: PrivateBComponent }]}
];@NgModule({imports: [ RouterModule.forRoot(routes) ],exports: [ RouterModule ],providers: [AuthService,UserService]
})
export class AppRoutingModule { }

canActivate, canActivateChild, and canDeactivate implement from AuthService. The service implementation will be shown shortly alongside the UserService implementation.

canActivatecanActivateChildcanDeactivate实现。 该服务实现将与UserService实现一起显示。

UserService provides the information needed to authenticate a user. The AuthService guard method implementations perform the authentication. AppRoutingModule must include the two services into its providers array. This is so the module’s injector knows how to instantiate them.

UserService提供验证用户所需的信息。 AuthService保护方法实现执行身份验证。 AppRoutingModule must include the two services into its providers array. This is so the module's injector knows how to instantiate them.

Nested routes exist off of the /private-nest path. The Route object for /private-nest contains a few more new fields. Their names should look familiar as they mirror their corresponding guard methods.

Nested routes exist off of the /private-nest path. The Route object for /private-nest contains a few more new fields. Their names should look familiar as they mirror their corresponding guard methods.

Each field fires its namesake’s method implementation inside of the service when triggered. Any number of services can populate this array too. The method implementation of each service gets tested. They must return a boolean value or an Observable that emits a boolean value.

Each field fires its namesake's method implementation inside of the service when triggered. Any number of services can populate this array too. The method implementation of each service gets tested. They must return a boolean value or an Observable that emits a boolean value.

See the AuthService and UserService implementations below.

See the AuthService and UserService implementations below.

// user.service.tsimport { Injectable } from '@angular/core';
import { Router } from '@angular/router';class TheUser {constructor(public isLoggedIn: boolean = false) { }toggleLogin() {this.isLoggedIn = true;}toggleLogout() {this.isLoggedIn = false;}
}const globalUser = new TheUser();@Injectable({providedIn: 'root'
})
export class UserService {theUser: TheUser = globalUser;constructor(private router: Router) { }get isLoggedIn() {return this.theUser.isLoggedIn;}login() {this.theUser.toggleLogin();}logout() {this.theUser.toggleLogout();this.router.navigate(['/']);}
}

The same instance of TheUser gets passed with each instantiation of UserService. TheUser provides access to isLoggedIn determining the user’s login status. Two other public methods let the UserService toggle the value of isLoggedIn. This is so the user can log in and out.

The same instance of TheUser gets passed with each instantiation of UserService. TheUser provides access to isLoggedIn determining the user's login status. Two other public methods let the UserService toggle the value of isLoggedIn . This is so the user can log in and out.

You can think of TheUser as a global instance. UserService is a instantiable interface that configures this global. Changes to TheUser from one UserService instantiation apply to every other UserService instance. UserService implements into AuthService to provide access to isLoggedIn of TheUser for authentication.

You can think of TheUser as a global instance. UserService is a instantiable interface that configures this global. Changes to TheUser from one UserService instantiation apply to every other UserService instance. UserService implements into AuthService to provide access to isLoggedIn of TheUser for authentication.

import { Component, Injectable } from '@angular/core';
import { CanActivate, CanActivateChild, CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';import { UserService } from './user.service';@Injectable({providedIn: 'root'
})
export class AuthService implements CanActivate, CanActivateChild, CanDeactivate<Component> {constructor(private user: UserService) {}canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {if (this.user.isLoggedIn)return true;elsereturn false;}canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {return this.canActivate(route, state);}canDeactivate(component: Component, route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {if (!this.user.isLoggedIn || window.confirm('Leave the nest?'))return true;elsereturn false;}
}

AuthService implements every guard method imported from @angular/router. Each guard method maps to a corresponding field in the PrivateNestComponent’s Route object. An instance of UserService instantiates from the AuthService constructor. AuthService determines if a user may proceed using isLoggedIn exposed by UserService.

AuthService implements every guard method imported from @angular/router . Each guard method maps to a corresponding field in the PrivateNestComponent's Route object. An instance of UserService instantiates from the AuthService constructor. AuthService determines if a user may proceed using isLoggedIn exposed by UserService.

Returning false from a guard instructs the route to block the user from routing. A return value of true lets the user proceed to his route destination. If more than one service authenticates, they all must return true to permit access. canActivateChild guards the child routes of PrivateNestComponent. This guard method accounts for users bypassing PrivateNestComponent through the address bar.

Returning false from a guard instructs the route to block the user from routing. A return value of true lets the user proceed to his route destination. If more than one service authenticates, they all must return true to permit access. canActivateChild guards the child routes of PrivateNestComponent. This guard method accounts for users bypassing PrivateNestComponent through the address bar.

Guard method parameters pass in automatically upon invocation. While the example does not make use of them, they do supply useful information from the route. The developer can use this information to help authenticate the user.

Guard method parameters pass in automatically upon invocation. While the example does not make use of them, they do supply useful information from the route. The developer can use this information to help authenticate the user.

AppComponent also instantiates UserService for direct use in its template. The UserService instantiation of AppComponent and AuthService reference the same user class (TheUser).

AppComponent also instantiates UserService for direct use in its template. The UserService instantiation of AppComponent and AuthService reference the same user class ( TheUser ).

import { Component } from '@angular/core';import { UserService } from './services/user.service';@Component({selector: 'app-root',template: `<ul><li routerLink="/private-nest">Enter the secret nest!</li><li routerLink="/">Leave the secret nest!</li><li *ngIf="user.isLoggedIn"><button (click)="user.logout()">LOGOUT</button></li><li *ngIf="!user.isLoggedIn"><button (click)="user.login()">LOGIN</button></li></ul><router-outlet></router-outlet>`
})
export class AppComponent {constructor(private user: UserService) { }
}

UserService handles all the logic for AppComponent. AppComponent mostly concerns its template. A UserService does instantiate as user from the class constructor. user data determines the template’s functionality.

UserService handles all the logic for AppComponent. AppComponent mostly concerns its template. A UserService does instantiate as user from the class constructor. user data determines the template's functionality.

结论 (Conclusions)

Routing strikes a fine balance between organizing and restricting sections of the application. A smaller application such as a blog or tribute page may not require any routing. Even then, including a little bit of hash routing could not hurt. A user may only want to reference part of the page after all.

Routing strikes a fine balance between organizing and restricting sections of the application. A smaller application such as a blog or tribute page may not require any routing. Even then, including a little bit of hash routing could not hurt. A user may only want to reference part of the page after all.

Angular applies its own routing library built on top of the HTML5 history API. This API omits hash routing to instead use the pushState(...) and replaceState(...) methods. They change the web address URL without refreshing the page. The default path location routing strategy in Angular works this way. Setting RouterModule.forRoot(routes, { useHash: true }) enables hash routing if preferred.

Angular applies its own routing library built on top of the HTML5 history API . This API omits hash routing to instead use the pushState(...) and replaceState(...) methods. They change the web address URL without refreshing the page. The default path location routing strategy in Angular works this way. Setting RouterModule.forRoot(routes, { useHash: true }) enables hash routing if preferred.

This article focused on the default path location strategy. Regardless of the strategy, many routing utilities are available to route an application. The RouterModule exposes these utilities through its exports. Basic, parameterized, nested, and guarded routes are all possible utilizing RouterModule.

This article focused on the default path location strategy. Regardless of the strategy, many routing utilities are available to route an application. The RouterModule exposes these utilities through its exports. Basic, parameterized, nested, and guarded routes are all possible utilizing RouterModule.

NgModules (NgModules)

Angular applications begin from the root NgModule. Angular manages an application’s dependencies through its module system comprised of NgModules. Alongside plain JavaScript modules, NgModules ensure code modularity and encapsulation.

Angular applications begin from the root NgModule. Angular manages an application's dependencies through its module system comprised of NgModules. Alongside plain JavaScript modules, NgModules ensure code modularity and encapsulation.

Modules also provide a top-most level of organizing code. Each NgModule sections off its own chunk of code as the root. This module provides top-to-bottom encapsulation for its code. The entire block of code can then export to any other module. In this sense, NgModules act like gatekeepers to their own code blocks.

Modules also provide a top-most level of organizing code. Each NgModule sections off its own chunk of code as the root. This module provides top-to-bottom encapsulation for its code. The entire block of code can then export to any other module. In this sense, NgModules act like gatekeepers to their own code blocks.

Angular’s documented utilities come from NgModules authored by Angular. No utility is available unless the NgModule that declares it gets included into the root. These utilities must also export from their host module so that importers can use them. This form of encapsulation empowers the developer to produce his or her own NgModules within the same file-system.

Angular's documented utilities come from NgModules authored by Angular. No utility is available unless the NgModule that declares it gets included into the root. These utilities must also export from their host module so that importers can use them. This form of encapsulation empowers the developer to produce his or her own NgModules within the same file-system.

Plus, it makes sense to know why the Angular CLI (command-line interface) imports BrowserModule from @angular/core. This happens whenever a new app generates using the CLI command: ng new [name-of-app].

Plus, it makes sense to know why the Angular CLI (command-line interface) imports BrowserModule from @angular/core . This happens whenever a new app generates using the CLI command: ng new [name-of-app] .

Understanding the point of the implementation may suffice in most cases. However, understanding how the implementation wires itself to the root is even better. It all happens automatically by importing BrowserModule into the root.

Understanding the point of the implementation may suffice in most cases. However, understanding how the implementation wires itself to the root is even better. It all happens automatically by importing BrowserModule into the root.

NgModule Decorator (NgModule Decorator)

Angular defines its modules by decorating a generic class. The @NgModule decorator indicates the class’ modular purpose to Angular. An NgModule class consolidates root dependencies accessible/instantiable from the module’s scope. ‘Scope’ meaning anything originating from the module’s metadata.

Angular defines its modules by decorating a generic class. The @NgModule decorator indicates the class' modular purpose to Angular. An NgModule class consolidates root dependencies accessible/instantiable from the module's scope. 'Scope' meaning anything originating from the module's metadata.

import { NgModule } from '@angular/core';@NgModule({// … metadata …
})
export class AppModule { }

NgModule Metadata (NgModule Metadata)

The CLI generated root NgModule includes the following metadata fields. These fields provide configuration to the code block upon which the NgModule presides.

The CLI generated root NgModule includes the following metadata fields. These fields provide configuration to the code block upon which the NgModule presides.

  • declarations: []

    declarations: []

  • imports: []

    imports: []

  • providers: []

    providers: []

  • bootstrap: []

    bootstrap: []

声明书 (Declarations)

The declarations array includes all components, directives, or pipes hosted by an NgModule. They are private to the module unless explicitly exported inside its metadata. Given this use-case, components, directives, and pipes are nicknamed ‘declarables’. An NgModule must declare a declarable uniquely. The declarable cannot declare twice in separate NgModules. An error gets thrown otherwise. See the below example.

The declarations array includes all components, directives, or pipes hosted by an NgModule. They are private to the module unless explicitly exported inside its metadata. Given this use-case, components, directives, and pipes are nicknamed 'declarables'. An NgModule must declare a declarable uniquely. The declarable cannot declare twice in separate NgModules. An error gets thrown otherwise. See the below example.

import { NgModule } from '@angular/core';
import { TwoComponent } from './components/two.component.ts';@NgModule({declarations: [ TwoComponent ]
})
export class TwoModule { }@NgModule({imports: [ TwoModule ],declarations: [ TwoComponent ]
})
export class OneModule { }

Angular throws an error for the sake of NgModule encapsulation. Declarables are private to the NgModule that declares them by default. If multiple NgModules need a certain declarable, they should import the declaring NgModule. This NgModule must then export the desired declarable so that the other NgModules can use it.

Angular throws an error for the sake of NgModule encapsulation. Declarables are private to the NgModule that declares them by default. If multiple NgModules need a certain declarable, they should import the declaring NgModule. This NgModule must then export the desired declarable so that the other NgModules can use it.

import { NgModule } from '@angular/core';
import { TwoComponent } from './components/two.component.ts';@NgModule({declarations: [ TwoComponent ],exports: [ TwoComponent ]
})
export class TwoModule { }@NgModule({imports: [ TwoModule ] // this module can now use TwoComponent
})
export class OneModule { }

The above example will not throw an error. TwoComponent has been uniquely declared between the two NgModules. OneModule also has access to TwoComponent since it imports TwoModule. TwoModule in turn exports the TwoComponent for external use.

The above example will not throw an error. TwoComponent has been uniquely declared between the two NgModules. OneModule also has access to TwoComponent since it imports TwoModule. TwoModule in turn exports the TwoComponent for external use.

Imports (Imports)

The imports array only accepts NgModules. This array does not accept declarables, services, or anything else besides other NgModules. Importing a module provides access to what declarable the module publicizes.

The imports array only accepts NgModules. This array does not accept declarables, services, or anything else besides other NgModules. Importing a module provides access to what declarable the module publicizes.

This explains why importing BrowserModule provides access to its various utilities. Each declarable utility declared in BrowserModule exports from its metadata. Upon importing BrowserModule, those exported declarables become available to the importing NgModule. Services do not export at all since they lack the same encapsulation.

This explains why importing BrowserModule provides access to its various utilities. Each declarable utility declared in BrowserModule exports from its metadata. Upon importing BrowserModule , those exported declarables become available to the importing NgModule. Services do not export at all since they lack the same encapsulation.

提供者 (Providers)

The lack of service encapsulation might seem odd given the encapsulation of declarables. Remember that services go into the providers array separate from declarations or exports.

The lack of service encapsulation might seem odd given the encapsulation of declarables. Remember that services go into the providers array separate from declarations or exports.

When Angular compiles, it flattens the root NgModule and its imports into one module. Services group together in a single providers array hosted by the merged NgModule. Declarables maintain their encapsulation through a set of compile-time flags.

When Angular compiles, it flattens the root NgModule and its imports into one module. Services group together in a single providers array hosted by the merged NgModule. Declarables maintain their encapsulation through a set of compile-time flags.

If NgModule providers contain matching token values, the importing root module takes precedence. Past that, the last NgModule imported takes precedence. See the next example. Pay special attention to the NgModule importing the other two. Recognize how that affects the precedence of the provided service.

If NgModule providers contain matching token values, the importing root module takes precedence. Past that, the last NgModule imported takes precedence. See the next example. Pay special attention to the NgModule importing the other two. Recognize how that affects the precedence of the provided service.

import { NgModule } from '@angular/core';@NgModule({providers: [ AwesomeService ], // 1st precedence + importing moduleimports: [BModule,CModule]
})
export class AModule { }@NgModule({providers: [ AwesomeService ]  // 3rd precedence + first import
})
export class BModule { }@NgModule({providers: [ AwesomeService ]  // 2nd precedence + last import
})
export class CModule { }

Instantiating AwesomeService from within AModule’s scope results in an AwesomeService instance as provided in AModule’s metadata. If AModule’s providers omitted this service, the AwesomeService of CModule would take precedence. So and so forth for BModule if CModule’s providers omitted AwesomeService.

Instantiating AwesomeService from within AModule's scope results in an AwesomeService instance as provided in AModule's metadata. If AModule's providers omitted this service, the AwesomeService of CModule would take precedence. So and so forth for BModule if CModule's providers omitted AwesomeService.

引导程序 (Bootstrap)

The bootstrap array accepts components. For each component of the Array, Angular inserts the component as its own root of the index.html file. The CLI-generated root NgModule of an application will always have this field.

The bootstrap array accepts components. For each component of the Array, Angular inserts the component as its own root of the index.html file. The CLI-generated root NgModule of an application will always have this field.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';@NgModule({declarations: [ AppComponent ],imports: [ BrowserModule ],providers: [],bootstrap: [ AppComponent ]
})
export class AppModule { }

AppComponent’s element will inject into the base-level HTML of the app (index.html). The rest of the component tree unfolds from there. The scope of the overarching NgModule covers this entire tree plus any others injected from the bootstrap array. The array usually contains only one element. This one component represents the module as a single element and its underlying tree.

AppComponent's element will inject into the base-level HTML of the app ( index.html ). The rest of the component tree unfolds from there. The scope of the overarching NgModule covers this entire tree plus any others injected from the bootstrap array. The array usually contains only one element. This one component represents the module as a single element and its underlying tree.

NgModules vs JavaScript Modules (NgModules vs JavaScript Modules)

You have seen Angular and JavaScript modules working together in the previous examples. The top-most import..from statements constitute the JavaScript module system. The file locations of each statement’s target must export a class, variable, or function matching the request. import { TARGET } from './path/to/exported/target'.

You have seen Angular and JavaScript modules working together in the previous examples. The top-most import..from statements constitute the JavaScript module system. The file locations of each statement's target must export a class, variable, or function matching the request. import { TARGET } from './path/to/exported/target' .

In JavaScript, modules are file-separated. Files import using the import..from keywords as just mentioned. NgModules, on the other hand, are class-separated and decorated with @NgModule. And so, many Angular modules can exist in a single file. This cannot happen with JavaScript since a file defines a module.

In JavaScript, modules are file-separated. Files import using the import..from keywords as just mentioned. NgModules, on the other hand, are class-separated and decorated with @NgModule . And so, many Angular modules can exist in a single file. This cannot happen with JavaScript since a file defines a module.

Granted, conventions say that each @NgModule decorated class should have its own file. Even so, know that files do not constitute their own modules in Angular. Classes decorated with @NgModule create that distinction.

Granted, conventions say that each @NgModule decorated class should have its own file. Even so, know that files do not constitute their own modules in Angular. Classes decorated with @NgModule create that distinction.

JavaScript modules provide token references to @NgModule metadata. This happens at the top of a file hosting a NgModule class. NgModule uses these tokens inside of its metadata fields (declarables, imports, providers, etc). The only reason @NgModule can decorate a class in the first place is because JavaScript imports it from the top of the file.

JavaScript modules provide token references to @NgModule metadata. This happens at the top of a file hosting a NgModule class. NgModule uses these tokens inside of its metadata fields (declarables, imports, providers, etc). The only reason @NgModule can decorate a class in the first place is because JavaScript imports it from the top of the file.

// JavaScript module system provides tokens
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { AppService } from './app.service';
// Javascript module system is strict about where it imports. It can only import at the top of files.// Angular NgModule uses those tokens in its metadata settings
@NgModule({ // import { NgModule } from '@angular/core';declarations: [AppComponent // import { AppComponent } from './app.component';],imports: [BrowserModule // import { BrowserModule } from '@angular/platform-browser';],providers: [AppService // import { AppService } from './app.service';],bootstrap: [AppComponent // import { AppComponent } from './app.component';]
})
export class AppModule { }
// JavaScript module system exports the class. Other modules can now import AppModule.

The above example does not introduce anything new. The focus here is on the comments explaining how the two modular systems work together. JavaScript provides token references while NgModule uses those token to encapsulate and configure its underlying block of code.

The above example does not introduce anything new. The focus here is on the comments explaining how the two modular systems work together. JavaScript provides token references while NgModule uses those token to encapsulate and configure its underlying block of code.

Feature Modules (Feature Modules)

Applications grow overtime. Scaling them properly requires application organization. A solid system for this will make further development much easier.

Applications grow overtime. Scaling them properly requires application organization. A solid system for this will make further development much easier.

In Angular, schematics ensure purpose-driven sections of code remain distinguishable. Beyond the sub-NgModule schematics, there are the NgModules themselves. They are a type of schematic too. They stand above the rest in the list of schematics excluding the application itself.

In Angular, schematics ensure purpose-driven sections of code remain distinguishable. Beyond the sub-NgModule schematics, there are the NgModules themselves. They are a type of schematic too. They stand above the rest in the list of schematics excluding the application itself.

The root module should not stand alone once an application starts to scale. Feature modules include any NgModule used alongside the root NgModule. You can think of the root module as having the bootstrap: [] metadata field. Feature application ensure the root module does not oversaturate its metadata.

The root module should not stand alone once an application starts to scale. Feature modules include any NgModule used alongside the root NgModule. You can think of the root module as having the bootstrap: [] metadata field. Feature application ensure the root module does not oversaturate its metadata.

Feature modules isolate a section of code on behalf of any importing module. They can handle whole application sections independently. This means it could be used in any application whose root module imports the feature module. This tactic saves developers time and effort over the course of multiple applications! It keeps the application’s root NgModule lean as well.

Feature modules isolate a section of code on behalf of any importing module. They can handle whole application sections independently. This means it could be used in any application whose root module imports the feature module. This tactic saves developers time and effort over the course of multiple applications! It keeps the application's root NgModule lean as well.

In the root NgModule of an app, adding a feature module’s token into the root’s imports array does the trick. Whatever the feature module exports or provides becomes available to the root.

In the root NgModule of an app, adding a feature module's token into the root's imports array does the trick. Whatever the feature module exports or provides becomes available to the root.

// ./awesome.module.tsimport { NgModule } from '@angular/core';
import { AwesomePipe } from './awesome/pipes/awesome.pipe';
import { AwesomeComponent } from './awesome/components/awesome.component';
import { AwesomeDirective } from './awesome/directives/awesome.directive';@NgModule({exports: [AwesomePipe,AwesomeComponent,AwesomeDirective]declarations: [AwesomePipe,AwesomeComponent,AwesomeDirective]
})
export class AwesomeModule { }
// ./app.module.tsimport { AwesomeModule } from './awesome.module';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';@NgModule({declarations: [AppComponent],imports: [AwesomeModule,BrowserModule],providers: [],bootstrap: [AppComponent]
})
export class AppModule { }
// ./app.component.tsimport { Component } from '@angular/core';@Component({selector: 'app-root',template: `<!-- AwesomeDirective --><h1 appAwesome>This element mutates as per the directive logic of appAwesome.</h1><!-- AwesomePipe --><p>Generic output: {{ componentData | awesome }}</p><section><!-- AwesomeComponent --><app-awesome></app-awesome></section>`
})
export class AppComponent {componentData: string = "Lots of transformable data!";
}

<app-awesome></app-awesome> (component), awesome (pipe), and appAwesome (directive) are exclusive to AwesomeModule. Had it not exported these declarables or AppModule neglected to add AwesomeModule to its imports, then AwesomeModule’s declarables would not have been usable by AppComponent’s template. AwesomeModule is a feature module to the root NgModule AppModule.

<app-awesome></app-awesome> (component), awesome (pipe), and appAwesome (directive) are exclusive to AwesomeModule. Had it not exported these declarables or AppModule neglected to add AwesomeModule to its imports, then AwesomeModule's declarables would not have been usable by AppComponent's template. AwesomeModule is a feature module to the root NgModule AppModule.

Angular provides some its own modules that supplement the root upon their importation. This is due to these feature modules exporting what they create.

Angular provides some its own modules that supplement the root upon their importation. This is due to these feature modules exporting what they create.

Static module methods (Static module methods)

Sometimes modules provide the option to be configured with a custom config object. This is achieved by leveraging static methods inside the module class.

Sometimes modules provide the option to be configured with a custom config object. This is achieved by leveraging static methods inside the module class.

An example of this approach is the RoutingModule which provides a .forRoot(...) method directly on the module.

An example of this approach is the RoutingModule which provides a .forRoot(...) method directly on the module.

To define your own static module method you add it to the module class using the static keyword. The return type has to be ModuleWithProviders.

To define your own static module method you add it to the module class using the static keyword. The return type has to be ModuleWithProviders .

// configureable.module.tsimport { AwesomeModule } from './awesome.module';
import { ConfigureableService, CUSTOM_CONFIG_TOKEN, Config } from './configurable.service';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';@NgModule({imports: [AwesomeModule,BrowserModule],providers: [ConfigureableService]
})
export class ConfigureableModule { static forRoot(config: Config): ModuleWithProviders {return {ngModule: ConfigureableModule,providers: [ConfigureableService,{provide: CUSTOM_CONFIG_TOKEN,useValue: config}]};}
}
// configureable.service.tsimport { Inject, Injectable, InjectionToken } from '@angular/core';export const CUSTOM_CONFIG_TOKEN: InjectionToken<string> = new InjectionToken('customConfig');export interface Config {url: string
}@Injectable()
export class ConfigureableService {constructor(@Inject(CUSTOM_CONFIG_TOKEN) private config: Config)
}

Notice that the object the forRoot(...) method returns is almost identical to the NgModule config.

Notice that the object the forRoot(...) method returns is almost identical to the NgModule config.

The forRoot(...) method accepts a custom config object that the user can provide when importing the module.

The forRoot(...) method accepts a custom config object that the user can provide when importing the module.

imports: [...ConfigureableModule.forRoot({ url: 'http://localhost' }),...
]

The config is then provided using a custom InjectionToken called CUSTOM_CONFIG_TOKEN and injected in the ConfigureableService. The ConfigureableModule should be imported only once using the forRoot(...) method. This provides the CUSTOM_CONFIG_TOKEN with the custom config. All other modules should import the ConfigureableModule without the forRoot(...) method.

The config is then provided using a custom InjectionToken called CUSTOM_CONFIG_TOKEN and injected in the ConfigureableService . The ConfigureableModule should be imported only once using the forRoot(...) method. This provides the CUSTOM_CONFIG_TOKEN with the custom config. All other modules should import the ConfigureableModule without the forRoot(...) method.

NgModule Examples from Angular (NgModule Examples from Angular)

Angular provides a variety of modules importable from @angular. Two of the most commonly imported modules are CommonModule and HttpClientModule.

Angular provides a variety of modules importable from @angular . Two of the most commonly imported modules are CommonModule and HttpClientModule .

CommonModule is actually a subset of BrowserModule. Both provide access to the *ngIf and *ngFor structural directives. BrowserModule includes a platform-specific installation for the web browser. CommonModule omits this installation. The BrowserModule should import into the root NgModule of a web application. CommonModule provides *ngIf and *ngFor to feature modules not requiring a platform installation.

CommonModule is actually a subset of BrowserModule . Both provide access to the *ngIf and *ngFor structural directives. BrowserModule includes a platform-specific installation for the web browser. CommonModule omits this installation. The BrowserModule should import into the root NgModule of a web application. CommonModule provides *ngIf and *ngFor to feature modules not requiring a platform installation.

HttpClientModule provides the HttpClient service. Remember that services go in the providers array of the @NgModule metadata. They are not declarable. During compilation, every NgModule gets consolidated into one single module. Services are not encapsulated unlike declarables. They are all instantiable through the root injector located alongside the merged NgModule.

HttpClientModule provides the HttpClient service. Remember that services go in the providers array of the @NgModule metadata. They are not declarable. During compilation, every NgModule gets consolidated into one single module. Services are not encapsulated unlike declarables. They are all instantiable through the root injector located alongside the merged NgModule.

Back to the point. Like any other service, HttpClient instantiates into a class through its constructor via dependency injection (DI). Using DI, the root injector injects an instance of HttpClient into the constructor. This service lets developers make HTTP requests with the service’s implementation.

Back to the point. Like any other service, HttpClient instantiates into a class through its constructor via dependency injection (DI). Using DI, the root injector injects an instance of HttpClient into the constructor. This service lets developers make HTTP requests with the service's implementation.

The HttpClient implementation includes into the HttpClientModule providers array. As long as the root NgModule imports HttpClientModule, HttpClient will instantiate from inside the root’s scope as expected.

The HttpClient implementation includes into the HttpClientModule providers array. As long as the root NgModule imports HttpClientModule , HttpClient will instantiate from inside the root's scope as expected.

结论 (Conclusion)

Chances are you may have already taken advantage of Angular’s NgModules. Angular makes it very easy to throw a module into the root NgModule’s imports array. Utilities are often exported from the imported module’s metadata. Hence why its utilities suddenly become available upon importation within the root NgModule.

Chances are you may have already taken advantage of Angular's NgModules. Angular makes it very easy to throw a module into the root NgModule's imports array. Utilities are often exported from the imported module's metadata. Hence why its utilities suddenly become available upon importation within the root NgModule.

NgModules work closely with plain JavaScript modules. One provides token while one uses them for configuration. Their teamwork results in a robust, modular system unique to the Angular framework. It provides a new layer of organization above all other schematics excluding the application.

NgModules work closely with plain JavaScript modules. One provides token while one uses them for configuration. Their teamwork results in a robust, modular system unique to the Angular framework. It provides a new layer of organization above all other schematics excluding the application.

Hopefully this article furthers your understanding of NgModules. Angular can leverage this system even further for some of the more exotic use-cases.

Hopefully this article furthers your understanding of NgModules. Angular can leverage this system even further for some of the more exotic use-cases.

翻译自: https://www.freecodecamp.org/news/angular-vs-angularjs/

flask框架视图和路由

flask框架视图和路由_角度视图,路由和NgModule的解释相关推荐

  1. mysql中 视图的优缺点_数据库视图优缺点分析

    视图作用是什么 所谓视图(View)其实是执行查询语句后得到的结果,但这个查询结果可以仿真成数据表来使用,所以有人也称它为"虚拟数据表",视图在操作上和数据表没有什么区别,但两者的 ...

  2. django 类视图装饰器_灵活视图处理的模式,第2部分–使用装饰器

    django 类视图装饰器 For years, we've been so busy discussing the virtues and drawbacks of the model/contro ...

  3. vue重复路由_解决vue路由name同名,路由重复的问题

    在项目中,想让路由后缀为空,或者index的时候,都跳转到路由为index的页面,于是在router中如下配置 routes: [{ path: '/', name: 'index', compone ...

  4. 哪个软件能代替斐讯路由_斐讯路由app下载-斐讯路由app安卓版下载v7.2.0-聚侠网...

    现在家里面都有路由器,斐讯路由就是一款功能强大的路由器app,方便用户们管理自己家中的路由器,而且通过此app可以防止其他用户们蹭网,保证自己的网速,还有超多小功能,就看用户们自己发现了,快来试试吧. ...

  5. 哪个软件能代替斐讯路由_斐讯路由(老版本无需登录)

    斐讯路由帮助现在还有着斐讯的用户对路由器进行控制,下面就由微侠网小编给您介绍这款软件,斐讯虽然倒闭了但是相比国内还是有着一定批量的用户进行使用,而现在随着服务器停止了服务,在新版的APP当中我也也不能 ...

  6. linux 软路由_树莓派软路由 OpenWrt 简介

    用树莓派 DIY 软路由的时候,总会说到 OpenWrt 这个开源 Linux 发行版.那 OpenWrt 到底是什么,为什么会用于树莓派软路由?这篇文章将会解答关于 OpenWrt 的一系列问题. ...

  7. bond添加路由_实验:路由、bonding、team

    实验:路由实验 路由表构成 目标网络:网络ID 子网掩码 接口:到达目标网络,从哪个接口出来,此接口 网关:gateway,下一个邻近路由器的邻近接口的IP地址,如果目标网络和本路由器直接,网关的地址 ...

  8. python flask框架详解

    Flask是一个Python编写的Web 微框架,让我们可以使用Python语言快速实现一个网站或Web服务.本文参考自Flask官方文档, 英文不好的同学也可以参考中文文档 1.安装flask pi ...

  9. Flask框架(1.flask概述,Windows配置 virtualenv虚拟环境步骤和路由以及视图函数的定义)

    框架:      软件框架,指的是为了实现某个业界标准或者完成特别基本任务的软件组件规范,也指为了实现某个软件组建规范时,提供规范所要求之基础功能的软件产品.       框架的功能类似于基础设施,提 ...

最新文章

  1. Java黑皮书课后题第8章:**8.11(游戏:九个硬币的正反面)一个3*3的矩阵中放置了9个硬币,这些硬币有些面朝上有朝下。1表示正面0表示反面,每个状态使用一个二进制数表示。使用十进制数表示状态
  2. plsql(轻量版)_触发器
  3. VR创投寻鹰会,一场深圳VR行业人的大聚会
  4. 转载:闲话权限设计三层境界
  5. 大道至简 第二章 读后随笔
  6. 蓝桥杯 波动数列 01背包
  7. Use Data Caching Techniques to Boost Performance and Ensure Synchronization(转)
  8. Openlayer:学习笔记之简单的ol.Map应用
  9. python设置excel格式_Python使用xlwt写excel并设置写入格式
  10. 用友 NC客户化开发手册
  11. 烟雨江南暂排第一,第四届橙瓜网络文学奖入围20年十佳奇幻大神
  12. 关于微星主板官网下载网卡驱动后,网络适配器消失的问题
  13. 银行的总行,营业部,支行,分理处有什么样的关系
  14. 模拟斗地主发牌功能的设计与实现
  15. forEach和$.each()以及$().each()的用法
  16. 碰撞检测中的K_DOPS算法的研究
  17. js实现web汉字笔画教学
  18. Linux常用工具大全
  19. 为什么要建立品牌?当然是为了让生意更好做。
  20. [Unity][安卓]VideoPlayer组件播放视频

热门文章

  1. 基本TCP套接字编程
  2. 三面美团Java岗,尚学堂java马士兵全套
  3. matlab确定位置,Hurlin 的PSTR模型包,怎样确定位置参数个数
  4. shutil模块、json和pickle模块
  5. 21天学通Visual.Basic pdf
  6. cs231n---语义分割 物体定位 物体检测 物体分割
  7. Linux下 安装Redis并配置服务
  8. Spark常见问题解决办法
  9. 数学笔记1——导数1(导数的基本概念)
  10. 一颗站在技术边缘的土豆