1580 lines
69 KiB
Plaintext
Raw Normal View History

2016-07-12 18:14:13 -07:00
block includes
include ../_util-fns
// TODO
Images
:marked
**Angular Modules** help organize an application into cohesive blocks of functionality.
**Angular模块**能帮你把应用组织成多个内聚的功能块儿。
2016-07-12 18:14:13 -07:00
An Angular Module is a _class_ adorned with the **@NgModule** decorator function.
`@NgModule` takes a metadata object that tells Angular how to compile and run module code.
It identifies the module's _own_ components, directives and pipes,
making some of them public so external components can use them.
It may add service providers to the application dependency injectors.
And there are more options covered here.
Merge remote-tracking branch 'origin/master' # Conflicts: # README.md # public/_includes/_scripts-include.jade # public/docs/_examples/homepage-hello-world/ts/index.1.html # public/docs/_examples/homepage-tabs/ts/index.1.html # public/docs/_examples/homepage-todo/ts/index.1.html # public/docs/_examples/systemjs.config.plunker.build.js # public/docs/_examples/systemjs.config.plunker.js # public/docs/js/latest/cookbook/ts-to-js.jade # public/docs/ts/latest/_data.json # public/docs/ts/latest/cookbook/component-communication.jade # public/docs/ts/latest/cookbook/component-relative-paths.jade # public/docs/ts/latest/glossary.jade # public/docs/ts/latest/guide/_data.json # public/docs/ts/latest/guide/architecture.jade # public/docs/ts/latest/guide/attribute-directives.jade # public/docs/ts/latest/guide/dependency-injection.jade # public/docs/ts/latest/guide/index.jade # public/docs/ts/latest/guide/lifecycle-hooks.jade # public/docs/ts/latest/guide/ngmodule.jade # public/docs/ts/latest/guide/npm-packages.jade # public/docs/ts/latest/guide/security.jade # public/docs/ts/latest/guide/server-communication.jade # public/docs/ts/latest/guide/template-syntax.jade # public/docs/ts/latest/guide/typescript-configuration.jade # public/docs/ts/latest/index.jade # public/docs/ts/latest/quickstart.jade # public/docs/ts/latest/tutorial/_data.json # public/docs/ts/latest/tutorial/toh-pt6.jade # public/features.jade # public/resources/js/directives/live-example.js # tools/plunker-builder/indexHtmlTranslator.js
2016-09-08 17:20:01 +08:00
Angular模块就是一个带有**@NgModule**装饰器函数的_类_。
`@NgModule`接收一个元数据对象该对象告诉Angular如何编译和运行模块代码。
它标记出该模块_拥有_的组件、指令和管道
把它们公开出去,以便外部组件使用它们。
它可以向应用的依赖注入器中添加服务提供商。
本页还会涉及到更多选项。
2016-07-12 18:14:13 -07:00
This page explains how to **create** `NgModule` classes and how to load them,
either immediately when the application launches or later, as needed, via the [Router](router.html).
Merge remote-tracking branch 'origin/master' # Conflicts: # README.md # public/_includes/_scripts-include.jade # public/docs/_examples/homepage-hello-world/ts/index.1.html # public/docs/_examples/homepage-tabs/ts/index.1.html # public/docs/_examples/homepage-todo/ts/index.1.html # public/docs/_examples/systemjs.config.plunker.build.js # public/docs/_examples/systemjs.config.plunker.js # public/docs/js/latest/cookbook/ts-to-js.jade # public/docs/ts/latest/_data.json # public/docs/ts/latest/cookbook/component-communication.jade # public/docs/ts/latest/cookbook/component-relative-paths.jade # public/docs/ts/latest/glossary.jade # public/docs/ts/latest/guide/_data.json # public/docs/ts/latest/guide/architecture.jade # public/docs/ts/latest/guide/attribute-directives.jade # public/docs/ts/latest/guide/dependency-injection.jade # public/docs/ts/latest/guide/index.jade # public/docs/ts/latest/guide/lifecycle-hooks.jade # public/docs/ts/latest/guide/ngmodule.jade # public/docs/ts/latest/guide/npm-packages.jade # public/docs/ts/latest/guide/security.jade # public/docs/ts/latest/guide/server-communication.jade # public/docs/ts/latest/guide/template-syntax.jade # public/docs/ts/latest/guide/typescript-configuration.jade # public/docs/ts/latest/index.jade # public/docs/ts/latest/quickstart.jade # public/docs/ts/latest/tutorial/_data.json # public/docs/ts/latest/tutorial/toh-pt6.jade # public/features.jade # public/resources/js/directives/live-example.js # tools/plunker-builder/indexHtmlTranslator.js
2016-09-08 17:20:01 +08:00
本页面将会讲解如何**创建**`NgModule`类,以及如何加载它们 —— 无论是在程序启动时立即加载,还是稍后由[路由器](router.html)按需加载。
2016-07-12 18:14:13 -07:00
## Contents
## 目录
2016-07-12 18:14:13 -07:00
* [Angular modularity](#angular-modularity "Add structure to the app with NgModule")
* [The application root module](#root-module "The startup module that every app requires")
* [Bootstrap](#bootstrap "Launch the app in a browser with the root module as the entry point") the root module
* [Declarations](#declarations "Declare the components, directives, and pipes that belong to a module")
* [Providers](#providers "Extend the app with additional services")
* [Imports](#imports "Import components, directives, and pipes for use in component templates")
* [Resolve conflicts](#resolve-conflicts "When two directives have the same selector ...")
* [Feature modules](#feature-modules "Partition the app into feature modules")
* [Lazy loaded modules](#lazy-load "Load modules asynchronously") with the Router
* [Shared modules](#shared-module "Create modules for commonly used components, directives, and pipes")
* [The Core module](#core-module "Create a core module with app-wide singleton services and single-use components")
* [Configure core services with _forRoot_](#core-for-root "Configure providers during module import")
* [Prevent reimport of the _CoreModule_](#prevent-reimport "because bad things happen if a lazy loaded module imports Core")
2016-07-12 18:14:13 -07:00
* [NgModule metadata properties](#ngmodule-properties "A technical summary of the @NgModule metadata properties")
### Live examples
This page explains Angular Modules through a progression of improvements to a sample with a "Tour of Heroes" theme.
Here's an index to live examples at key moments in the evolution of that sample:
* <live-example plnkr="minimal.0">A minimal NgModule app</live-example>
* <live-example plnkr="contact.1b">The first contact module</live-example>
* <live-example plnkr="contact.2">The revised contact module</live-example>
* <live-example plnkr="pre-shared.3">Just before adding _SharedModule_</live-example>
* <live-example>The final version</live-example>
### Frequently Asked Questions (FAQs)
This page covers Angular Module concepts in a tutorial fashion.
The companion [Angular Module FAQs](../cookbook/ngmodule-faq.html "Angular Module FAQs") cookbook
offers ready answers to specific design and implementation questions.
Read this page first before hopping over to those FAQs.
.l-hr
2016-07-12 18:14:13 -07:00
a#angular-modularity
.l-main-section
:marked
## Angular Modularity
## Angular模块化
2016-07-12 18:14:13 -07:00
Modules are a great way to organize the application and extend it with capabilities from external libraries.
模块是组织应用程序和使用外部程序库的最佳途径。
2016-07-12 18:14:13 -07:00
Many Angular libraries are modules (e.g, `FormsModule`, `HttpModule`, `RouterModule`).
Many third party libraries are available as Angular modules (e.g.,
<a href="https://material.angular.io/" target="_blank">Material Design</a>,
<a href="http://ionicframework.com/" target="_blank">Ionic</a>,
<a href="https://github.com/angular/angularfire2" target="_blank">AngularFire2</a>).
很多Angular库都是模块比如`FormsModule`、`HttpModule`、`RouterModule`。
很多第三方库也封装成了Angular模块比如<a href="https://material.angular.io/" target="_blank">Material Design</a>、
<a href="http://ionicframework.com/" target="_blank">Ionic</a>、
<a href="https://github.com/angular/angularfire2" target="_blank">AngularFire2</a>。
2016-07-12 18:14:13 -07:00
Angular modules consolidate components, directives and pipes into
cohesive blocks of functionality, each focused on a
feature area, application business domain, workflow, or common collection of utilities.
Angular模块把组件、指令和管道打包成内聚的功能块儿每一个都聚焦于一个特性分区、业务领域、工作流或一组通用的工具。
2016-07-12 18:14:13 -07:00
Modules can also add services to the application.
Such services might be internally-developed such as the application logger.
They can come from outside sources such as the Angular router and Http client.
模块还能用来把服务加到应用程序中。这些服务可能是内部研发的比如应用日志服务也可能是外部资源比如Angular路由和Http客户端。
2016-07-12 18:14:13 -07:00
Modules can be loaded eagerly when the application starts.
They can also be _lazy loaded_ asynchronously by the router.
模块可能在应用启动时立即加载也可能由路由器进行异步_延迟加载_。
2016-07-12 18:14:13 -07:00
An Angular module is a class decorated with `@NgModule` metadata. The metadata:
Angular模块是一个由`@NgModule`装饰器提供元数据的类,元数据包括:
2016-07-12 18:14:13 -07:00
* declare which components, directives and pipes _belong_ to the module.
* 声明哪些组件、指令、管道属于该模块。
2016-07-12 18:14:13 -07:00
* make some of those classes public so that other component templates can use them.
* 公开某些类,以便其它的组件模板可以使用它们。
* import other modules with the components, directives and pipes needed by the components in _this_ module.
Merge remote-tracking branch 'origin/master' # Conflicts: # README.md # public/_includes/_scripts-include.jade # public/docs/_examples/homepage-hello-world/ts/index.1.html # public/docs/_examples/homepage-tabs/ts/index.1.html # public/docs/_examples/homepage-todo/ts/index.1.html # public/docs/_examples/systemjs.config.plunker.build.js # public/docs/_examples/systemjs.config.plunker.js # public/docs/js/latest/cookbook/ts-to-js.jade # public/docs/ts/latest/_data.json # public/docs/ts/latest/cookbook/component-communication.jade # public/docs/ts/latest/cookbook/component-relative-paths.jade # public/docs/ts/latest/glossary.jade # public/docs/ts/latest/guide/_data.json # public/docs/ts/latest/guide/architecture.jade # public/docs/ts/latest/guide/attribute-directives.jade # public/docs/ts/latest/guide/dependency-injection.jade # public/docs/ts/latest/guide/index.jade # public/docs/ts/latest/guide/lifecycle-hooks.jade # public/docs/ts/latest/guide/ngmodule.jade # public/docs/ts/latest/guide/npm-packages.jade # public/docs/ts/latest/guide/security.jade # public/docs/ts/latest/guide/server-communication.jade # public/docs/ts/latest/guide/template-syntax.jade # public/docs/ts/latest/guide/typescript-configuration.jade # public/docs/ts/latest/index.jade # public/docs/ts/latest/quickstart.jade # public/docs/ts/latest/tutorial/_data.json # public/docs/ts/latest/tutorial/toh-pt6.jade # public/features.jade # public/resources/js/directives/live-example.js # tools/plunker-builder/indexHtmlTranslator.js
2016-09-08 17:20:01 +08:00
* 导入其它模块,以获得本模块所需的组件、指令和管道。
2016-07-12 18:14:13 -07:00
* provide services at the application level that any application component can use.
* 在应用程序级提供服务,以便应用中的任何组件都能使用它。
2016-07-12 18:14:13 -07:00
Every Angular app has at least one module class, the _root module_.
We bootstrap that module to launch the application.
每个Angular应用至少有一个模块类 —— _根模块_我们将通过引导根模块来启动应用。
2016-07-12 18:14:13 -07:00
The _root module_ is all we need in a simple application with a few components.
As the app grows, we refactor the _root module_ into **feature modules**
that represent collections of related functionality.
We then import these modules into the _root module_.
对于组件很少的简单应用来说只用一个_根模块_就足够了。
随着应用规模的增长我们逐步把_根模块_重构成一些**特性模块**,它们用来实现一组密切相关的功能。
然后我们在_根模块_中导入它们。
2016-07-12 18:14:13 -07:00
We'll see how later in the page. Let's start with the _root module_.
稍后我们就会看到怎么做。不过我们还是先从_根模块_开始吧
2016-07-12 18:14:13 -07:00
a#root_module
.l-main-section
:marked
## _AppModule_ - the application root module
## _AppModule_ - 应用的根模块
2016-07-12 18:14:13 -07:00
Every Angular app has a **root module** class.
By convention it's a class called `AppModule` in a file named `app.module.ts`.
每个Angular应用都有一个**根模块**类。
按照约定,它的类名叫做`AppModule`,被放在`app.module.ts`文件中。
2016-07-12 18:14:13 -07:00
This `AppModule` is about as minimal as it gets:
这个最小化的`AppModule`是这样的:
2016-07-12 18:14:13 -07:00
+makeExample('ngmodule/ts/app/app.module.0.ts', '', 'app/app.module.ts (minimal)')(format=".")
2016-07-12 18:14:13 -07:00
:marked
2016-08-10 10:00:08 +08:00
The `@NgModule` decorator defines the metadata for the module.
2016-07-12 18:14:13 -07:00
We'll take an intuitive approach to understanding the metadata and fill in details as we go.
`@NgModel`装饰器用来为模块定义元数据。
我们先凭直觉来理解一下元数据,接下来再逐步深入细节。
2016-07-12 18:14:13 -07:00
This metadata imports a single helper module, `BrowserModule`, the module every browser app must import.
这个元数据只导入了一个辅助模块,`BrowserModule`,每个运行在浏览器中的应用都必须导入它。
2016-07-12 18:14:13 -07:00
`BrowserModule` registers critical application service providers.
It also includes common directives like `NgIf` and `NgFor` which become immediately visible and usable
in any of this modules component templates.
`BrowserModule`注册了一些关键的应用服务提供商。
它还包括了一些通用的指令,比如`NgIf`和`NgFor`,所以这些指令在该模块的任何组件模板中都是可用的。
2016-07-12 18:14:13 -07:00
The `declarations` list identifies the application's only component,
the _root component_, the top of this app's rather bare component tree.
`declarations`列出了该应用程序中唯一的组件(根组件),这是该应用的顶层组件,而不会暴露出整个组件树。
2016-07-12 18:14:13 -07:00
The example `AppComponent` simply displays a data-bound title:
这个范例`AppComponent`只会显示出一个被绑定的标题:
2016-07-12 18:14:13 -07:00
+makeExample('ngmodule/ts/app/app.component.0.ts', '', 'app/app.component.ts (minimal)')(format=".")
:marked
Lastly, the `@NgModule.bootstrap` property identifies this `AppComponent` as the _bootstrap component_.
When Angular launches the app, it places the HTML rendering of `AppComponent` in the DOM,
inside the `<my-app>` element tags of the `index.html`
最后,`@NgModule.bootstrap`属性把这个`AppComponent`标记为_引导bootstrap组件_。
当Angular引导应用时它会在DOM中渲染`AppComponent`,并把结果放进`index.html`的`<my-app>`元素标记内部。
2016-07-12 18:14:13 -07:00
a#bootstrap
.l-main-section
:marked
## Bootstrapping in _main.ts_
## 在_main.ts_中引导
2016-07-12 18:14:13 -07:00
We launch the application by bootstrapping the `AppModule` in the `main.ts` file.
在`main.ts`文件中,我们通过引导`AppModule`来启动应用。
2016-07-12 18:14:13 -07:00
Angular offers a variety of bootstrapping options, targeting multiple platforms.
In this page we consider two options, both targeting the browser.
针对不同的平台Angular提供了很多引导选项。
Merge remote-tracking branch 'origin/master' # Conflicts: # README.md # public/_includes/_scripts-include.jade # public/docs/_examples/homepage-hello-world/ts/index.1.html # public/docs/_examples/homepage-tabs/ts/index.1.html # public/docs/_examples/homepage-todo/ts/index.1.html # public/docs/_examples/systemjs.config.plunker.build.js # public/docs/_examples/systemjs.config.plunker.js # public/docs/js/latest/cookbook/ts-to-js.jade # public/docs/ts/latest/_data.json # public/docs/ts/latest/cookbook/component-communication.jade # public/docs/ts/latest/cookbook/component-relative-paths.jade # public/docs/ts/latest/glossary.jade # public/docs/ts/latest/guide/_data.json # public/docs/ts/latest/guide/architecture.jade # public/docs/ts/latest/guide/attribute-directives.jade # public/docs/ts/latest/guide/dependency-injection.jade # public/docs/ts/latest/guide/index.jade # public/docs/ts/latest/guide/lifecycle-hooks.jade # public/docs/ts/latest/guide/ngmodule.jade # public/docs/ts/latest/guide/npm-packages.jade # public/docs/ts/latest/guide/security.jade # public/docs/ts/latest/guide/server-communication.jade # public/docs/ts/latest/guide/template-syntax.jade # public/docs/ts/latest/guide/typescript-configuration.jade # public/docs/ts/latest/index.jade # public/docs/ts/latest/quickstart.jade # public/docs/ts/latest/tutorial/_data.json # public/docs/ts/latest/tutorial/toh-pt6.jade # public/features.jade # public/resources/js/directives/live-example.js # tools/plunker-builder/indexHtmlTranslator.js
2016-09-08 17:20:01 +08:00
本页面我们只讲两个选项,都是针对浏览器平台的。
2016-07-12 18:14:13 -07:00
### Dynamic bootstrapping with the Just-In-Time (JIT) compiler
### 通过即时JIT编译器动态引导
In the first, _dynamic_ option, the [Angular compiler](../cookbook/ngmodule-faq.html#q-angular-compiler "About the Angular Compiler")
2016-07-12 18:14:13 -07:00
compiles the application in the browser and then launches the app.
Merge remote-tracking branch 'origin/master' # Conflicts: # README.md # public/_includes/_scripts-include.jade # public/docs/_examples/homepage-hello-world/ts/index.1.html # public/docs/_examples/homepage-tabs/ts/index.1.html # public/docs/_examples/homepage-todo/ts/index.1.html # public/docs/_examples/systemjs.config.plunker.build.js # public/docs/_examples/systemjs.config.plunker.js # public/docs/js/latest/cookbook/ts-to-js.jade # public/docs/ts/latest/_data.json # public/docs/ts/latest/cookbook/component-communication.jade # public/docs/ts/latest/cookbook/component-relative-paths.jade # public/docs/ts/latest/glossary.jade # public/docs/ts/latest/guide/_data.json # public/docs/ts/latest/guide/architecture.jade # public/docs/ts/latest/guide/attribute-directives.jade # public/docs/ts/latest/guide/dependency-injection.jade # public/docs/ts/latest/guide/index.jade # public/docs/ts/latest/guide/lifecycle-hooks.jade # public/docs/ts/latest/guide/ngmodule.jade # public/docs/ts/latest/guide/npm-packages.jade # public/docs/ts/latest/guide/security.jade # public/docs/ts/latest/guide/server-communication.jade # public/docs/ts/latest/guide/template-syntax.jade # public/docs/ts/latest/guide/typescript-configuration.jade # public/docs/ts/latest/index.jade # public/docs/ts/latest/quickstart.jade # public/docs/ts/latest/tutorial/_data.json # public/docs/ts/latest/tutorial/toh-pt6.jade # public/features.jade # public/resources/js/directives/live-example.js # tools/plunker-builder/indexHtmlTranslator.js
2016-09-08 17:20:01 +08:00
先看看_dynamic_选项[Angular编译器](../cookbook/ngmodule-faq.html#q-angular-compiler "关于Angular编译器")在浏览器中编译并引导该应用。
2016-07-12 18:14:13 -07:00
+makeExample('ngmodule/ts/app/main.ts', '', 'app/main.ts (dynamic)')(format=".")
2016-07-12 18:14:13 -07:00
:marked
The samples in this page demonstrate the dynamic bootstrapping approach.
2016-07-12 18:14:13 -07:00
这里的例子演示进行动态引导的方法。
<live-example embedded plnkr="minimal.0" img="devguide/ngmodule/minimal-plunker.png">Try the live example.</live-example>
2016-07-12 18:14:13 -07:00
Merge remote-tracking branch 'origin/master' # Conflicts: # README.md # public/_includes/_scripts-include.jade # public/docs/_examples/homepage-hello-world/ts/index.1.html # public/docs/_examples/homepage-tabs/ts/index.1.html # public/docs/_examples/homepage-todo/ts/index.1.html # public/docs/_examples/systemjs.config.plunker.build.js # public/docs/_examples/systemjs.config.plunker.js # public/docs/js/latest/cookbook/ts-to-js.jade # public/docs/ts/latest/_data.json # public/docs/ts/latest/cookbook/component-communication.jade # public/docs/ts/latest/cookbook/component-relative-paths.jade # public/docs/ts/latest/glossary.jade # public/docs/ts/latest/guide/_data.json # public/docs/ts/latest/guide/architecture.jade # public/docs/ts/latest/guide/attribute-directives.jade # public/docs/ts/latest/guide/dependency-injection.jade # public/docs/ts/latest/guide/index.jade # public/docs/ts/latest/guide/lifecycle-hooks.jade # public/docs/ts/latest/guide/ngmodule.jade # public/docs/ts/latest/guide/npm-packages.jade # public/docs/ts/latest/guide/security.jade # public/docs/ts/latest/guide/server-communication.jade # public/docs/ts/latest/guide/template-syntax.jade # public/docs/ts/latest/guide/typescript-configuration.jade # public/docs/ts/latest/index.jade # public/docs/ts/latest/quickstart.jade # public/docs/ts/latest/tutorial/_data.json # public/docs/ts/latest/tutorial/toh-pt6.jade # public/features.jade # public/resources/js/directives/live-example.js # tools/plunker-builder/indexHtmlTranslator.js
2016-09-08 17:20:01 +08:00
<live-example embedded plnkr="minimal.0" img="devguide/ngmodule/minimal-plunker.png">试试在线例子。</live-example>
2016-07-12 18:14:13 -07:00
### Static bootstrapping with the Ahead-Of-Time (AOT) compiler
### 使用预先编译器AOT - Ahead-Of-Time进行静态引导
2016-07-12 18:14:13 -07:00
Consider the static alternative which can produce a much smaller application that
launches faster, especially on mobile devices and high latency networks.
静态方案可以生成更小、启动更快的应用,建议优先使用它,特别是在移动设备或高延迟网络下。
2016-07-12 18:14:13 -07:00
In the _static_ option, the Angular compiler runs ahead of time as part of the build process,
producing a collection of class factories in their own files.
Among them is the `AppModuleNgFactory`.
使用_static_选项Angular编译器作为构建流程的一部分提前运行生成一组类工厂。它们的核心就是`AppModuleNgFactory`。
2016-07-12 18:14:13 -07:00
The syntax for bootstrapping the pre-compiled `AppModuleNgFactory` is similar to
the dynamic version that bootstraps the `AppModule` class.
引导预编译的`AppModuleNgFactory`的语法和动态引导`AppModule`类的方式很相似。
2016-07-12 18:14:13 -07:00
+makeExample('ngmodule/ts/app/main-static.ts', '', 'app/main.ts (static)')(format=".")
2016-07-12 18:14:13 -07:00
:marked
Because the entire application was pre-compiled,
we don't ship the _Angular Compiler_ to the browser and we don't compile in the browser.
由于整个应用都是预编译的所以我们不用把_Angular编译器_一起发到浏览器中也不用在浏览器中进行编译。
2016-07-12 18:14:13 -07:00
The application code downloaded to the browser is much smaller than the dynamic equivalent
and it is ready to execute immediately. The performance boost can be significant.
下载到浏览器中的应用代码比动态版本要小得多,并且能立即执行。引导的性能能得到显著提升。
2016-07-12 18:14:13 -07:00
Both the JIT and AOT compilers generate an `AppModuleNgFactory` class from the same `AppModule` source code.
The JIT compiler creates that factory class on the fly, in memory, in the browser.
The AOT compiler outputs the factory to a physical file
that we're importing here in the static version of `main.ts`.
无论是JIT还是AOT编译器都会从同一份`AppModule`源码中生成一个`AppModuleNgFactory`类。
JIT编译器动态、在内存中、在浏览器中创建这个工厂类。
AOT编译器把工厂输出成一个物理文件也就是我们在静态版本`main.ts`中导入的那个。
2016-07-12 18:14:13 -07:00
In general, the `AppModule` should neither know nor care how it is bootstrapped.
通常,`AppModule`不必关心它将被如何引导。
2016-07-12 18:14:13 -07:00
Although the `AppModule` evolves as the app grows, the bootstrap code in `main.ts` doesn't change.
This is the last time we'll look at `main.ts`.
虽然`AppModule`会随着应用而演化,但是`main.ts`中的引导代码不会变。
这将是我们最后一次关注`main.ts`了。
2016-07-12 18:14:13 -07:00
.l-hr
a#declarations
.l-main-section
:marked
## Declare directives and components
## 声明指令和组件
2016-07-12 18:14:13 -07:00
The app evolves.
The first addition is a `HighlightDirective`, an [attribute directive](attribute-directives.html)
that sets the background color of the attached element.
应用继续演进。
首先加入的是`HighlightDirective`,一个[属性型指令](attribute-directives.html),它会设置所在元素的背景色。
2016-07-12 18:14:13 -07:00
+makeExample('ngmodule/ts/app/highlight.directive.ts', '', 'app/highlight.directive.ts')(format=".")
2016-07-12 18:14:13 -07:00
:marked
We update the `AppComponent` template to attach the directive to the title:
我们更新`AppComponent`的模板,来把该指令附加到标题上:
2016-07-12 18:14:13 -07:00
+makeExample('ngmodule/ts/app/app.component.1.ts', 'template')(format=".")
:marked
If we ran the app now, Angular would not recognize the `highlight` attribute and would ignore it.
2016-07-12 18:14:13 -07:00
We must declare the directive in `AppModule`.
Merge remote-tracking branch 'origin/master' # Conflicts: # README.md # public/_includes/_scripts-include.jade # public/docs/_examples/homepage-hello-world/ts/index.1.html # public/docs/_examples/homepage-tabs/ts/index.1.html # public/docs/_examples/homepage-todo/ts/index.1.html # public/docs/_examples/systemjs.config.plunker.build.js # public/docs/_examples/systemjs.config.plunker.js # public/docs/js/latest/cookbook/ts-to-js.jade # public/docs/ts/latest/_data.json # public/docs/ts/latest/cookbook/component-communication.jade # public/docs/ts/latest/cookbook/component-relative-paths.jade # public/docs/ts/latest/glossary.jade # public/docs/ts/latest/guide/_data.json # public/docs/ts/latest/guide/architecture.jade # public/docs/ts/latest/guide/attribute-directives.jade # public/docs/ts/latest/guide/dependency-injection.jade # public/docs/ts/latest/guide/index.jade # public/docs/ts/latest/guide/lifecycle-hooks.jade # public/docs/ts/latest/guide/ngmodule.jade # public/docs/ts/latest/guide/npm-packages.jade # public/docs/ts/latest/guide/security.jade # public/docs/ts/latest/guide/server-communication.jade # public/docs/ts/latest/guide/template-syntax.jade # public/docs/ts/latest/guide/typescript-configuration.jade # public/docs/ts/latest/index.jade # public/docs/ts/latest/quickstart.jade # public/docs/ts/latest/tutorial/_data.json # public/docs/ts/latest/tutorial/toh-pt6.jade # public/features.jade # public/resources/js/directives/live-example.js # tools/plunker-builder/indexHtmlTranslator.js
2016-09-08 17:20:01 +08:00
如果我们现在就运行该应用Angular将无法识别`highlight`属性,而是会忽略它。
我们必须在`AppModule`中声明该指令。
2016-07-12 18:14:13 -07:00
Import the `HighlightDirective` class and add it to the module's `declarations` like this:
导入`HighlightDirective`类,并把它加入该模块的`declarations`数组中,就像这样:
2016-07-12 18:14:13 -07:00
+makeExample('ngmodule/ts/app/app.module.1.ts', 'directive')(format=".")
:marked
### Add a component
### 添加组件
2016-07-12 18:14:13 -07:00
We decide to refactor the title into its own `TitleComponent`.
The component's template binds to the component's `title` and `subtitle` properties like this:
我们打算把标题重构进独立的`TitleComponent`中去。
该组件的模板绑定到了组件的`title`和`subtitle`属性中,就像这样:
2016-07-12 18:14:13 -07:00
+makeExample('ngmodule/ts/app/title.component.html', 'v1', 'app/title.component.html')(format=".")
+makeExample('ngmodule/ts/app/title.component.ts', 'v1', 'app/title.component.ts')(format=".")
:marked
We rewrite the `AppComponent` to display the new `TitleComponent` in the `<app-title>` element,
using an input binding to set the `subtitle`.
我们重写了`AppComponent`来把这个新的`TitleComponent`显示到`<app-title>`元素中,并使用一个输入型绑定来设置`subtitle`。
2016-07-12 18:14:13 -07:00
+makeExample('ngmodule/ts/app/app.component.1.ts', '', 'app/app.component.ts (v1)')(format=".")
:marked
Angular won't recognize the `<app-title>` tag until we declare it in `AppModule`.
Import the `TitleComponent` class and add it to the module's `declarations`:
除非我们在`AppModule`中声明过否则Angular无法识别`<app-title>`标签。
导入`TitleComponent`类,并把它加到模块的`declarations`中:
2016-07-12 18:14:13 -07:00
+makeExample('ngmodule/ts/app/app.module.1.ts', 'component')(format=".")
a#providers
.l-main-section
:marked
## Service Providers
## 服务提供商
2016-07-12 18:14:13 -07:00
Modules are a great way to provide services for all of the module's components.
模块是为模块中的所有组件提供服务的最佳途径。
2016-07-12 18:14:13 -07:00
The [Dependency Injection](dependency-injection.html) page describes
2016-07-12 18:14:13 -07:00
the Angular hierarchical dependency injection system and how to configure that system
with [providers](dependency-injection.html#providers) at different levels of the
application's component tree.
[依赖注入](dependency-injection.html)一章中讲过Angular的层次化依赖注入系统
以及如何在组件树的不同层次上通过[提供商](dependency-injection.html#providers)配置该系统。
2016-07-12 18:14:13 -07:00
A module can add providers to the application's root dependency injector, making those services
available everywhere in the application.
模块可以往应用的“根依赖注入器”中添加提供商,让那些服务在应用中到处可用。
2016-07-12 18:14:13 -07:00
Many applications capture information about the currently logged-in user and make that information
accessible through a user service.
This sample application has a dummy implementation of such a `UserService`.
很多应用都需要获取当前登录的用户的信息并且通过user服务来访问它们。
该范例中有一个`UserService`的伪实现。
2016-07-12 18:14:13 -07:00
+makeExample('ngmodule/ts/app/user.service.ts', '', 'app/user.service.ts')(format=".")
:marked
The sample application should display a welcome message to the logged in user just below the application title.
Update the `TitleComponent` template to show the welcome message below the application title.
该范例应用会在标题下方为已登录用户显示一条欢迎信息。
更新`TitleComponent`的模板来显示它。
2016-07-12 18:14:13 -07:00
+makeExample('ngmodule/ts/app/title.component.html', '', 'app/title.component.html')(format=".")
:marked
Update the `TitleComponent` class with a constructor that injects the `UserService`
and sets the component's `user` property from the service.
更新`TitleComponent`,为它加入一个构造函数,注入`UserService`类,并把组件的`user`属性设置为它的实例。
2016-07-12 18:14:13 -07:00
+makeExample('ngmodule/ts/app/title.component.ts', '', 'app/title.component.ts')(format=".")
:marked
We've _defined_ and _used_ the service. Now we _provide_ it for all components to use by
adding it to a `providers` property in the `AppModule` metadata:
我们已经_定义_并_使用了_该服务。现在我们通过把它加入`AppModule`元数据中的`providers`属性来把它_提供_给所有组件使用。
2016-07-12 18:14:13 -07:00
+makeExample('ngmodule/ts/app/app.module.1.ts', 'providers', 'app/app.module.ts (providers)')(format=".")
a#imports
.l-main-section
:marked
## Import supporting modules
2016-08-27 07:57:18 +08:00
## 导入“支持模块”
2016-07-12 18:14:13 -07:00
The app shouldn't welcome a user if there is no user.
2016-08-27 07:57:18 +08:00
在没有当前用户时,显然不应该显示欢迎界面。
2016-07-12 18:14:13 -07:00
Notice in the revised `TitleComponent` that an `*ngIf` directive guards the message.
There is no message if there is no user.
2016-08-27 07:57:18 +08:00
注意,在修改过的`TitleComponent`中,有一个`*ngIf`指令在“守卫着”该消息。如果没有当前用户,就没有任何消息。
2016-07-12 18:14:13 -07:00
+makeExample('ngmodule/ts/app/title.component.html', 'ngIf', 'app/title.component.html (ngIf)')(format=".")
2016-08-27 07:57:18 +08:00
2016-07-12 18:14:13 -07:00
:marked
Although `AppModule` doesn't declare `NgIf`, the application still compiles and runs.
How can that be? The Angular compiler should either ignore or complain about unrecognized HTML.
2016-08-27 07:57:18 +08:00
虽然`AppModule`没有声明过`NgIf`指令但该应用仍然能正常编译和运行。为什么这样没问题呢Angular的编译器遇到不认识的HTML时应该不是忽略就是报错才对。
2016-07-12 18:14:13 -07:00
Angular _does_ recognize `NgIf` because we imported it earlier.
The initial version of `AppModule` imports `BrowserModule`.
2016-08-27 07:57:18 +08:00
Angular能识别`NfIf`指令,是因为我们以前导入过它。最初版本的`AppModule`就导入了`BrowserModule`。
2016-07-12 18:14:13 -07:00
+makeExample('ngmodule/ts/app/app.module.0.ts', 'imports', 'app/app.module.ts (imports)')(format=".")
2016-08-27 07:57:18 +08:00
2016-07-12 18:14:13 -07:00
:marked
Importing `BrowserModule` made all of its public components, directives and pipes visible
to the component templates in `AppModule`. They are ready to use without further ado.
2016-08-27 07:57:18 +08:00
导入`BrowserModule`会让该模块公开的所有组件、指令和管道在`AppModule`下的任何组件模板中直接可用,而不需要额外的繁琐步骤。
2016-07-12 18:14:13 -07:00
.l-sub-section
:marked
More accurately, `NgIf` is declared in `CommonModule` from `@angular/common`.
2016-08-27 07:57:18 +08:00
更准确的说,`NgIf`是在来自`@angular/common`的`CommonModule`中声明的。
2016-07-12 18:14:13 -07:00
`CommonModule` contributes many of the common directives that applications need including `ngIf` and `ngFor`.
2016-08-27 07:57:18 +08:00
`CommonModule`提供了很多应用程序中常用的指令,包括`NgIf`和`NgFor`等。
`BrowserModule` imports `CommonModule` and [_re-exports_](../cookbook/ngmodule-faq.html#q-re-export) it.
2016-07-12 18:14:13 -07:00
The net effect is that an importer of `BrowserModule` gets `CommonModule` directives automatically.
2016-08-27 07:57:18 +08:00
Merge remote-tracking branch 'origin/master' # Conflicts: # README.md # public/_includes/_scripts-include.jade # public/docs/_examples/homepage-hello-world/ts/index.1.html # public/docs/_examples/homepage-tabs/ts/index.1.html # public/docs/_examples/homepage-todo/ts/index.1.html # public/docs/_examples/systemjs.config.plunker.build.js # public/docs/_examples/systemjs.config.plunker.js # public/docs/js/latest/cookbook/ts-to-js.jade # public/docs/ts/latest/_data.json # public/docs/ts/latest/cookbook/component-communication.jade # public/docs/ts/latest/cookbook/component-relative-paths.jade # public/docs/ts/latest/glossary.jade # public/docs/ts/latest/guide/_data.json # public/docs/ts/latest/guide/architecture.jade # public/docs/ts/latest/guide/attribute-directives.jade # public/docs/ts/latest/guide/dependency-injection.jade # public/docs/ts/latest/guide/index.jade # public/docs/ts/latest/guide/lifecycle-hooks.jade # public/docs/ts/latest/guide/ngmodule.jade # public/docs/ts/latest/guide/npm-packages.jade # public/docs/ts/latest/guide/security.jade # public/docs/ts/latest/guide/server-communication.jade # public/docs/ts/latest/guide/template-syntax.jade # public/docs/ts/latest/guide/typescript-configuration.jade # public/docs/ts/latest/index.jade # public/docs/ts/latest/quickstart.jade # public/docs/ts/latest/tutorial/_data.json # public/docs/ts/latest/tutorial/toh-pt6.jade # public/features.jade # public/resources/js/directives/live-example.js # tools/plunker-builder/indexHtmlTranslator.js
2016-09-08 17:20:01 +08:00
`BrowserModule`导入了`CommonModule`并且[_重新导出_](../cookbook/ngmodule-faq.html#q-re-export)了它。
2016-08-27 07:57:18 +08:00
最终的效果是:只要导入`BrowserModule`就自动获得了`CommonModule`中的指令。
2016-07-12 18:14:13 -07:00
:marked
Many familiar Angular directives do not belong to`CommonModule`.
For example, `NgModel` and `RouterLink` belong to Angular's `FormsModule` and `RouterModule` respectively.
We must _import_ those modules before we can use their directives.
2016-08-27 07:57:18 +08:00
很多熟悉的Angular指令并不属于`CommonModule`。
例如,`NgModel`和`RouterLink`分别属于Angular的`FormsModule`模块和`RouterModule`模块。
在使用那些指令之前我们也必须_导入_那些模块。
2016-07-12 18:14:13 -07:00
To illustrate this point, we extend the sample app with `ContactComponent`,
a form component that imports form support from the Angular `FormsModule`.
2016-08-27 07:57:18 +08:00
要解释这一点,我们可以再加入一个`ContactComponent`组件它是一个表单组件从Angular的`FormsModule`中导入了表单支持。
2016-07-12 18:14:13 -07:00
### Add the _ContactComponent_
2016-08-27 07:57:18 +08:00
### 添加_ContactComponent_
2016-07-12 18:14:13 -07:00
[Angular Forms](forms.html) are a great way to manage user data entry.
2016-08-27 07:57:18 +08:00
[Angular表单](forms.html)是用来管理用户数据输入的最佳方式之一。
2016-07-12 18:14:13 -07:00
The `ContactComponent` presents a "contact editor",
implemented with _Angular Forms_ in the [_template-driven form_](forms.html) style.
2016-08-27 07:57:18 +08:00
`ContactComponnet`组件展现一个“联系人编辑器”,它是用[_模板驱动式表单_](forms.html)实现的。
2016-07-12 18:14:13 -07:00
.l-sub-section
:marked
#### Angular Form Styles
2016-08-27 07:57:18 +08:00
#### Angular表单的风格
2016-07-12 18:14:13 -07:00
We write Angular form components in either the
[_template-driven form_](forms.html) style or
the [_reactive form_](../cookbook/dynamic-form.html) style.
2016-08-27 07:57:18 +08:00
我们写Angular表单组件时或者使用[_模板驱动式表单_](forms.html)
或者使用[_响应式表单_](../cookbook/dynamic-form.html)。
2016-07-12 18:14:13 -07:00
This sample is about to import the `FormsModule` from `@angular/forms` because
the `ContactComponent` is written in the _template-driven_ style.
Modules with components written in the _reactive_ style,
should import the `ReactiveFormsModule` instead.
2016-08-27 07:57:18 +08:00
该例子中从`@angular/forms`中导入了`FormsModule`,这是因为`ContactComponent`组件用的是_模板驱动式_的。
那些带有_响应式表单_组件的模块就应该转而导入`ReactiveFormsModule`了。
2016-07-12 18:14:13 -07:00
:marked
The `ContactComponent` selector matches an element named `<app-contact>`.
Add an element with that name to the `AppComponent` template just below the `<app-title>`:
2016-08-27 07:57:18 +08:00
`ContactComponent`的选择器会去匹配名叫`<app-contact>`的元素。
请在`AppComponent`模板中`<app-title>`的下方添加一个具有此名字的元素:
2016-07-12 18:14:13 -07:00
+makeExample('ngmodule/ts/app/app.component.1b.ts', 'template', 'app/app.component.ts (template)')(format=".")
:marked
The `ContactComponent` has a lot going on.
Form components are often complex anyway and this one has its own `ContactService`,
its own [custom pipe](#pipes.html#custom-pipes) called `Awesome`,
and an alternative version of the `HighlightDirective`.
2016-08-27 07:57:18 +08:00
`ContactComponent`还有很多事要做。
表单组件通常都是很复杂的,而这一个则具有自己的它自己的`ContactService`、
它自己的[自定义管道](#pipes.html#custom-pipes) `Awesome`
以及`HighlightDirective`的另一个版本。
2016-07-12 18:14:13 -07:00
To make it manageable, we place all contact-related material in an `app/contact` folder
and break the component into three constituent HTML, TypeScript, and css files:
2016-08-27 07:57:18 +08:00
为了方便管理,我们把所有与联系人相关的编程元素都放进`app/contact`目录,
并把该组件分解成三个基本成分HTML、TypeScript和CSS文件
2016-07-12 18:14:13 -07:00
+makeTabs(
`ngmodule/ts/app/contact/contact.component.html,
ngmodule/ts/app/contact/contact.component.3.ts,
2016-07-12 18:14:13 -07:00
ngmodule/ts/app/contact/contact.component.css,
ngmodule/ts/app/contact/contact.service.ts,
ngmodule/ts/app/contact/awesome.pipe.ts,
ngmodule/ts/app/contact/highlight.directive.ts
`,
null,
`app/contact/contact.component.html,
app/contact/contact.component.ts,
app/contact/contact.component.css,
app/contact/contact.service.ts,
app/contact/awesome.pipe.ts,
app/contact/highlight.directive.ts
`)
:marked
Focus on the component template.
Notice the two-way data binding `[(ngModel)]` in the middle of the template.
`ngModel` is the selector for the `NgModel` directive.
2016-08-27 07:57:18 +08:00
先来看组件模板。
注意模板中部的双向数据绑定`[(ngModel)]`。
`ngModel`是`NgModel`指令的选择器。
2016-07-12 18:14:13 -07:00
Although `NgModel` is an Angular directive, the _Angular Compiler_ won't recognize it
because (a) `AppModule` doesn't declare it and (b) it wasn't imported via `BrowserModule`.
2016-08-27 07:57:18 +08:00
虽然`NgModel`是一个Angular指令但_Angular编译器_并不会识别它
这是因为:(a) `AppModule`没有声明过它,并且 (b) 它也没有通过`BrowserModule`被导入过。
2016-07-12 18:14:13 -07:00
Less obviously, even if Angular somehow recognized `ngModel`,
this `ContactComponent` would not behave like an Angular form because
form features such as validation are not yet available.
2016-08-27 07:57:18 +08:00
退一步说即使Angular有办法识别`ngModel``ContactComponent`也不会表现的像一个Angular表单
因为这里没法使用Form相关的特性比如有效性验证。
2016-07-12 18:14:13 -07:00
### Import the FormsModule
2016-08-27 07:57:18 +08:00
### 导入`FormsModule`
2016-07-12 18:14:13 -07:00
Add the `FormsModule` to the `AppModule` metadata's `imports` list.
2016-08-27 07:57:18 +08:00
把`FormsModule`加到`AppModule`元数据中的`imports`列表中:
2016-07-12 18:14:13 -07:00
+makeExample('ngmodule/ts/app/app.module.1.ts', 'imports')(format=".")
:marked
Merge remote-tracking branch 'origin/master' # Conflicts: # README.md # public/_includes/_scripts-include.jade # public/docs/_examples/homepage-hello-world/ts/index.1.html # public/docs/_examples/homepage-tabs/ts/index.1.html # public/docs/_examples/homepage-todo/ts/index.1.html # public/docs/_examples/systemjs.config.plunker.build.js # public/docs/_examples/systemjs.config.plunker.js # public/docs/js/latest/cookbook/ts-to-js.jade # public/docs/ts/latest/_data.json # public/docs/ts/latest/cookbook/component-communication.jade # public/docs/ts/latest/cookbook/component-relative-paths.jade # public/docs/ts/latest/glossary.jade # public/docs/ts/latest/guide/_data.json # public/docs/ts/latest/guide/architecture.jade # public/docs/ts/latest/guide/attribute-directives.jade # public/docs/ts/latest/guide/dependency-injection.jade # public/docs/ts/latest/guide/index.jade # public/docs/ts/latest/guide/lifecycle-hooks.jade # public/docs/ts/latest/guide/ngmodule.jade # public/docs/ts/latest/guide/npm-packages.jade # public/docs/ts/latest/guide/security.jade # public/docs/ts/latest/guide/server-communication.jade # public/docs/ts/latest/guide/template-syntax.jade # public/docs/ts/latest/guide/typescript-configuration.jade # public/docs/ts/latest/index.jade # public/docs/ts/latest/quickstart.jade # public/docs/ts/latest/tutorial/_data.json # public/docs/ts/latest/tutorial/toh-pt6.jade # public/features.jade # public/resources/js/directives/live-example.js # tools/plunker-builder/indexHtmlTranslator.js
2016-09-08 17:20:01 +08:00
Now `[(ngModel)]` binding will work and the user input will be validated by Angular Forms,
once we declare our new component, pipe and directive.
Merge remote-tracking branch 'origin/master' # Conflicts: # README.md # public/_includes/_scripts-include.jade # public/docs/_examples/homepage-hello-world/ts/index.1.html # public/docs/_examples/homepage-tabs/ts/index.1.html # public/docs/_examples/homepage-todo/ts/index.1.html # public/docs/_examples/systemjs.config.plunker.build.js # public/docs/_examples/systemjs.config.plunker.js # public/docs/js/latest/cookbook/ts-to-js.jade # public/docs/ts/latest/_data.json # public/docs/ts/latest/cookbook/component-communication.jade # public/docs/ts/latest/cookbook/component-relative-paths.jade # public/docs/ts/latest/glossary.jade # public/docs/ts/latest/guide/_data.json # public/docs/ts/latest/guide/architecture.jade # public/docs/ts/latest/guide/attribute-directives.jade # public/docs/ts/latest/guide/dependency-injection.jade # public/docs/ts/latest/guide/index.jade # public/docs/ts/latest/guide/lifecycle-hooks.jade # public/docs/ts/latest/guide/ngmodule.jade # public/docs/ts/latest/guide/npm-packages.jade # public/docs/ts/latest/guide/security.jade # public/docs/ts/latest/guide/server-communication.jade # public/docs/ts/latest/guide/template-syntax.jade # public/docs/ts/latest/guide/typescript-configuration.jade # public/docs/ts/latest/index.jade # public/docs/ts/latest/quickstart.jade # public/docs/ts/latest/tutorial/_data.json # public/docs/ts/latest/tutorial/toh-pt6.jade # public/features.jade # public/resources/js/directives/live-example.js # tools/plunker-builder/indexHtmlTranslator.js
2016-09-08 17:20:01 +08:00
一旦我们声明了这些新组件、管道和指令,`[(ngModel)]`绑定就会正常工作用户的输入也能被Angular表单验证了。
2016-07-12 18:14:13 -07:00
.alert.is-critical
:marked
**Do not** add `NgModel` &mdash; or the `FORMS_DIRECTIVES` &mdash;
to the `AppModule` metadata's declarations!
2016-08-27 07:57:18 +08:00
**不要**把`NgModel`(或`FORMS_DIRECTIVES加到`AppModule`元数据的`declarations`数据中!
2016-07-12 18:14:13 -07:00
These directives belong to the `FormsModule`.
Components, directives and pipes belong to one module &mdash; and _one module only_.
2016-08-27 07:57:18 +08:00
这些指令属于`FormsModule`。
组件、指令和管道应该而且只能属于一个模块。
2016-07-12 18:14:13 -07:00
**Never re-declare classes that belong to another module.**
2016-08-27 07:57:18 +08:00
**永远不要再次声明属于其它模块的类。**
2016-07-12 18:14:13 -07:00
a#declare-pipe
:marked
### Declare the contact component, directive and pipe
2016-08-27 07:57:18 +08:00
### 声明联系人的组件、指令和管道
2016-07-12 18:14:13 -07:00
The application fails to compile until we declare the contact component, directive and pipe.
Update the `declarations` in the `AppModule` accordingly:
2016-08-27 07:57:18 +08:00
如果我们没有声明该联系人模块的组件、指令和管道,该应用就会失败。
更新`AppModule`中的`declarations`元数据,就像这样:
2016-07-12 18:14:13 -07:00
+makeExample('ngmodule/ts/app/app.module.1.ts', 'declarations', 'app/app.module.ts (declarations)')(format=".")
a#import-name-conflict
.l-sub-section
:marked
There are two directives with the same name, both called `HighlightDirective`.
2016-08-27 07:57:18 +08:00
如果有两个同名指令,都叫做`HighlightDirective`,该怎么办呢?
2016-07-12 18:14:13 -07:00
We work around it by creating an alias for the second, contact version using the `as` JavaScript import keyword:
2016-08-27 07:57:18 +08:00
我们只要在import时使用`as`关键字来为第二个指令创建个别名就可以了。
2016-07-12 18:14:13 -07:00
+makeExample('ngmodule/ts/app/app.module.1b.ts', 'import-alias')(format=".")
:marked
This solves the immediate problem of referencing both directive _types_ in the same file but
leaves another problem unresoved as we discuss [below](#resolve-conflicts).
2016-08-27 07:57:18 +08:00
这解决了在文件中使用指令_类型_时的冲突问题但是还有另一个问题问题没有解决我们将在[后面](#resolve-conflicts)讨论它。
2016-07-12 18:14:13 -07:00
:marked
### Provide the _ContactService_
2016-08-27 07:57:18 +08:00
### 提供_ContactService_
2016-07-12 18:14:13 -07:00
The `ContactComponent` displays contacts retrieved by the `ContactService`
which Angular injects into its constructor.
2016-08-27 07:57:18 +08:00
`ContactComponent`显示从`ContactService`服务中获取的联系人信息该服务是被Angular注入到组件的构造函数中的。
2016-07-12 18:14:13 -07:00
We have to provide that service somewhere.
The `ContactComponent` _could_ provide it.
But then it would be scoped to this component _only_.
We want to share this service with other contact-related components that we will surely add later.
2016-08-27 07:57:18 +08:00
我们得在某个地方提供该服务。
在`ContactComponent`中_可以_提供它。
但是那样一来它的作用范围就会_仅_局限于该组件及其子组件。
而我们希望让该服务与其它和联系人有关的组件中共享,稍后我们就会添加那些组件。
2016-07-12 18:14:13 -07:00
In this app we chose to add `ContactService` to the `AppModule` metadata's `providers` list:
2016-08-27 07:57:18 +08:00
在此应用中,我们选择把`ContactSerivce`添加到`AppModule`元数据的`providers`列表中:
2016-07-12 18:14:13 -07:00
+makeExample('ngmodule/ts/app/app.module.1b.ts', 'providers', 'app/app.module.ts (providers)')(format=".")
:marked
Now `ContactService` (like `UserService`) can be injected into any component in the application.
2016-08-27 07:57:18 +08:00
现在,`ContactService`服务就能被注入进该应用中的任何组件了,就像`UserService`一样。
2016-07-12 18:14:13 -07:00
a#application-scoped-providers
.l-sub-section
:marked
#### Application-scoped Providers
2016-08-27 07:57:18 +08:00
#### 全应用范围的提供商
The `ContactService` provider is _application_-scoped because Angular
registers a module's `providers` with the application's **root injector**.
`ContactService`的提供商是_全应用_范围的这是因为Angular使用该应用的**根注入器**注册模块的`providers`。
2016-07-12 18:14:13 -07:00
2016-08-27 07:57:18 +08:00
Architecturally, the `ContactService` belongs to the Contact business domain.
Classes in _other_ domains don't need the `ContactService` and shouldn't inject it.
从架构上看,`ContactService`属于“联系人”这个业务领域。
_其它_领域中的类并不需要知道`ContactService`,也不会要求注入它。
We might expect Angular to offer a _module_-scoping mechanism to enforce this design.
It doesn't. Angular module instances, unlike components, do not have their own injectors
so they can't have their own provider scopes.
我们可能会期待Angular提供一种_模块_范围内的机制来保障此设计。
但它没有。与组件不同Angular的模块实例并没有它们自己的注入器所以它们也没有自己的供应商范围。
2016-07-12 18:14:13 -07:00
2016-08-27 07:57:18 +08:00
This omission is intentional.
Angular modules are designed primarily to extend an application,
to enrich the entire app with the module's capabilities.
Angular是故意这么设计的。
Angular的模块设计主要目的是扩展应用程序丰富其模块化能力。
Service scoping is rarely a problem in practice.
Non-contact components can't inject the `ContactService` by accident.
To inject `ContactService`, you must first import its _type_.
Only Contact components should import the `ContactService` _type_.
在实践中,服务的范围很少会成为问题。
联系人之外的组件不会意外注入`ContactService`服务。
要想注入`ContactService`你得先导入它的_类型_。
而只有联系人组件才会导入`ContactService`)类型_。
2016-07-12 18:14:13 -07:00
Merge remote-tracking branch 'origin/master' # Conflicts: # README.md # public/_includes/_scripts-include.jade # public/docs/_examples/homepage-hello-world/ts/index.1.html # public/docs/_examples/homepage-tabs/ts/index.1.html # public/docs/_examples/homepage-todo/ts/index.1.html # public/docs/_examples/systemjs.config.plunker.build.js # public/docs/_examples/systemjs.config.plunker.js # public/docs/js/latest/cookbook/ts-to-js.jade # public/docs/ts/latest/_data.json # public/docs/ts/latest/cookbook/component-communication.jade # public/docs/ts/latest/cookbook/component-relative-paths.jade # public/docs/ts/latest/glossary.jade # public/docs/ts/latest/guide/_data.json # public/docs/ts/latest/guide/architecture.jade # public/docs/ts/latest/guide/attribute-directives.jade # public/docs/ts/latest/guide/dependency-injection.jade # public/docs/ts/latest/guide/index.jade # public/docs/ts/latest/guide/lifecycle-hooks.jade # public/docs/ts/latest/guide/ngmodule.jade # public/docs/ts/latest/guide/npm-packages.jade # public/docs/ts/latest/guide/security.jade # public/docs/ts/latest/guide/server-communication.jade # public/docs/ts/latest/guide/template-syntax.jade # public/docs/ts/latest/guide/typescript-configuration.jade # public/docs/ts/latest/index.jade # public/docs/ts/latest/quickstart.jade # public/docs/ts/latest/tutorial/_data.json # public/docs/ts/latest/tutorial/toh-pt6.jade # public/features.jade # public/resources/js/directives/live-example.js # tools/plunker-builder/indexHtmlTranslator.js
2016-09-08 17:20:01 +08:00
See the [FAQ that pursues this issue](../cookbook/ngmodule-faq.html#q-component-scoped-providers)
and its mitigations in greater detail.
2016-08-27 07:57:18 +08:00
Merge remote-tracking branch 'origin/master' # Conflicts: # README.md # public/_includes/_scripts-include.jade # public/docs/_examples/homepage-hello-world/ts/index.1.html # public/docs/_examples/homepage-tabs/ts/index.1.html # public/docs/_examples/homepage-todo/ts/index.1.html # public/docs/_examples/systemjs.config.plunker.build.js # public/docs/_examples/systemjs.config.plunker.js # public/docs/js/latest/cookbook/ts-to-js.jade # public/docs/ts/latest/_data.json # public/docs/ts/latest/cookbook/component-communication.jade # public/docs/ts/latest/cookbook/component-relative-paths.jade # public/docs/ts/latest/glossary.jade # public/docs/ts/latest/guide/_data.json # public/docs/ts/latest/guide/architecture.jade # public/docs/ts/latest/guide/attribute-directives.jade # public/docs/ts/latest/guide/dependency-injection.jade # public/docs/ts/latest/guide/index.jade # public/docs/ts/latest/guide/lifecycle-hooks.jade # public/docs/ts/latest/guide/ngmodule.jade # public/docs/ts/latest/guide/npm-packages.jade # public/docs/ts/latest/guide/security.jade # public/docs/ts/latest/guide/server-communication.jade # public/docs/ts/latest/guide/template-syntax.jade # public/docs/ts/latest/guide/typescript-configuration.jade # public/docs/ts/latest/index.jade # public/docs/ts/latest/quickstart.jade # public/docs/ts/latest/tutorial/_data.json # public/docs/ts/latest/tutorial/toh-pt6.jade # public/features.jade # public/resources/js/directives/live-example.js # tools/plunker-builder/indexHtmlTranslator.js
2016-09-08 17:20:01 +08:00
参见[关于此问题的FAQ](../cookbook/ngmodule-faq.html#q-component-scoped-providers)
那里有非常详细的讲解。
2016-07-12 18:14:13 -07:00
:marked
### Run the app
2016-08-27 07:57:18 +08:00
### 运行该应用
2016-07-12 18:14:13 -07:00
Everything is now in place to run the application with its contact editor.
2016-08-27 07:57:18 +08:00
一切就绪,可以运行该应用及其联系人编辑器了。
2016-07-12 18:14:13 -07:00
The app file structure looks like this:
2016-08-27 07:57:18 +08:00
应用的文件结构是这样的:
2016-07-12 18:14:13 -07:00
.filetree
.file app
.children
.file app.component.ts
.file app.module.ts
.file highlight.directive.ts
.file main.ts
.file title.component.(html|ts)
.file user.service.ts
.file contact
.children
.file awesome.pipe.ts
.file contact.component.(css|html|ts)
.file contact.service.ts
.file highlight.directive.ts
:marked
Try the example:
<live-example embedded plnkr="contact.1b" img="devguide/ngmodule/contact-1b-plunker.png"></live-example>
2016-07-12 18:14:13 -07:00
2016-08-27 07:57:18 +08:00
<live-example plnkr="contact.1b">试试这个在线例子</live-example>
2016-07-12 18:14:13 -07:00
a#resolve-conflicts
.l-main-section
2016-07-12 18:14:13 -07:00
:marked
## Resolve directive conflicts
2016-08-27 07:57:18 +08:00
## 解决指令冲突
2016-07-12 18:14:13 -07:00
We ran into trouble [above](#import-name-conflict) when we declared the contact's `HighlightDirective` because
we already had a `HighlightDirective` class at the application level.
2016-08-27 07:57:18 +08:00
[以前](#import-name-conflict)我们要声明联系人的`HighlightDirective`指令时遇到了问题,因为在应用程序一级已经有了一个`HighlightDirective`类。
2016-07-12 18:14:13 -07:00
That both directives have the same name smells of trouble.
2016-08-27 07:57:18 +08:00
两个指令都用同一个名字让人不爽。
2016-07-12 18:14:13 -07:00
A look at their selectors reveals that they both highlight the attached element with a different color.
2016-08-27 07:57:18 +08:00
在查找它们的选择器时,它们都试图用不同的颜色来高亮所依附的元素。
2016-07-12 18:14:13 -07:00
+makeTabs(
`ngmodule/ts/app/highlight.directive.ts,
ngmodule/ts/app/contact/highlight.directive.ts`,
'',
`app/highlight.directive.ts,
app/contact/highlight.directive.ts`)
:marked
Will Angular use only one of them? No.
Both directives are declared in this module so _both directives are active_.
2016-08-27 07:57:18 +08:00
Angular会只用它们中的一个吗不会。
所有指令都声明在该模块中所以_这两个指令都会被激活_。
2016-07-12 18:14:13 -07:00
When the two directives compete to color the same element,
the directive declared later wins because its DOM changes overwrite the first.
In this case, the contact's `HighlightDirective` colors the application title text blue
when it should stay gold.
2016-08-27 07:57:18 +08:00
当两个指令在同一个元素上争相设置颜色时后声明的那个会胜出因为它对DOM的修改覆盖了前一个。
在该例子中,联系人的`HighlightDirective`把应用标题的文本染成了蓝色,而我们原本期望它保持金色。
2016-07-12 18:14:13 -07:00
.l-sub-section
:marked
The real problem is that there are _two different classes_ trying to do the same thing.
2016-08-27 07:57:18 +08:00
真正的问题在于有_两个不同的类_视图做同一件事。
2016-07-12 18:14:13 -07:00
It's OK to import the _same_ directive class multiple times.
Angular removes duplicate classes and only registers one of them.
2016-08-27 07:57:18 +08:00
多次导入_同一个_指令是没问题的Angular会移除重复的类而只注册一次。
2016-07-12 18:14:13 -07:00
But these are actually two different classes, defined in different files, that happen to have the same name.
2016-08-27 07:57:18 +08:00
但是这里实际上有两个不同的类,定义在不同的文件中,只是恰好有相同的名字。
2016-07-12 18:14:13 -07:00
They're not duplicates from Angular's perspective. Angular keeps both directives and
they take turns modifying the same HTML element.
2016-08-27 07:57:18 +08:00
从Angular的角度看两个类并没有重复。Angular会同时保留这两个指令并让它们依次修改同一个HTML元素。
2016-07-12 18:14:13 -07:00
:marked
At least the app still compiles.
If we define two different component classes with the same selector specifying the same element tag,
the compiler reports an error. It can't insert two components in the same DOM location.
2016-08-27 07:57:18 +08:00
至少,应用仍然编译通过了。
如果我们使用相同的选择器定义了两个不同的组件类并指定了同一个元素标记编译器就会报错说它无法在同一个DOM位置插入两个不同的组件。
2016-07-12 18:14:13 -07:00
What a mess!
2016-08-27 07:57:18 +08:00
真乱!
2016-07-12 18:14:13 -07:00
We can eliminate component and directive conflicts by creating feature modules
that insulate the declarations in one module from the declarations in another.
2016-08-27 07:57:18 +08:00
我们可以通过创建特性模块来消除组件与指令的冲突。
特性模块可以把来自一个模块中的声明和来自另一个的区隔开。
2016-07-12 18:14:13 -07:00
a#feature-modules
.l-main-section
:marked
## Feature Modules
2016-08-27 07:57:18 +08:00
## 特性模块
2016-07-12 18:14:13 -07:00
This application isn't big yet. But it's already suffering structural problems.
2016-08-27 07:57:18 +08:00
该应用还不大,但是已经在受结构方面的问题困扰了。
2016-07-12 18:14:13 -07:00
* The root `AppModule` grows larger with each new application class and shows no signs of stopping.
2016-08-27 07:57:18 +08:00
* 随着一个个类被加入应用中,根模块`AppModule`变大了,并且看起来还会继续变大。
2016-07-12 18:14:13 -07:00
* We have conflicting directives.
2016-08-27 07:57:18 +08:00
* 我们遇到了指令冲突。
2016-07-12 18:14:13 -07:00
The `HighlightDirective` in contact is re-coloring the work done by the `HighlightDirective` declared in `AppModule`.
And it's coloring the application title text when it should only color the `ContactComponent`.
2016-08-27 07:57:18 +08:00
联系人模块的`HighlightDirective`在`AppModule`中声明的`HighlightDirective`的基础上进行了二次上色。
并且,它染了应用标题文字的颜色,而不仅仅是`ContactComponent`中的。
2016-07-12 18:14:13 -07:00
* The app lacks clear boundaries between contact functionality and other application features.
That lack of clarity makes it harder to assign development responsibilities to different teams.
2016-08-27 07:57:18 +08:00
* 该应用在联系人和其它特性区之间缺乏清晰的边界。
这种缺失,导致难以在不同的开发组之间分配职责。
2016-07-12 18:14:13 -07:00
We mitigate these problems with _feature modules_.
2016-08-27 07:57:18 +08:00
我们用_特性模块_技术来缓解此问题。
2016-07-12 18:14:13 -07:00
### _Feature Module_
2016-08-27 07:57:18 +08:00
### _特性模块_
2016-07-12 18:14:13 -07:00
A _feature module_ is a class adorned by the `@NgModule` decorator and its metadata,
just like a root module.
Feature module metadata have the same properties as the metadata for a root module.
2016-08-27 07:57:18 +08:00
_特性模块_是一个带有`@NgModule`装饰器及其元数据的类,就像根模块中一样。
特性模块的元数据和根模块的元数据携带的属性是一样的。
2016-07-12 18:14:13 -07:00
The root module and the feature module share the same execution context.
They share the same dependency injector which means the services in one module
are available to all.
2016-08-27 07:57:18 +08:00
根模块和特性模块还共享着相同的执行环境。
它们共享着同一个依赖注入器,这意味着某个模块中定义的服务在所有模块中也都能用。
2016-07-12 18:14:13 -07:00
There are two significant technical differences:
2016-08-27 07:57:18 +08:00
它们在技术上有两个显著的不同点:
2016-07-12 18:14:13 -07:00
1. We _boot_ the root module to _launch_ the app;
we _import_ a feature module to _extend_ the app.
2016-08-27 07:57:18 +08:00
1. 我们_引导_根模块来_启动_应用但_导入_特性模块来_扩展_应用。
2016-07-12 18:14:13 -07:00
2. A feature module can expose or hide its implementation from other modules.
2016-08-27 07:57:18 +08:00
2. 特性模块可以暴露或隐藏来自其它模块的实现。
2016-07-12 18:14:13 -07:00
Otherwise, a feature module is distinguished primarily by its intent.
2016-08-27 07:57:18 +08:00
此外,特性模块主要还是从它的设计意图上来区分。
A feature module delivers a cohesive set of functionality
2016-07-12 18:14:13 -07:00
focused on an application business domain, a user workflow, a facility (forms, http, routing),
or a collection of related utilities.
2016-08-27 07:57:18 +08:00
特性模块用来提供一组紧密相关的功能。
聚焦于应用的某个业务领域、用户的某个工作流、某个基础设施表单、HTTP、路由或一组相互关联的工具。
2016-07-12 18:14:13 -07:00
While we can do everything within the root module,
feature modules help us partition the app into areas of specific interest and purpose.
2016-08-27 07:57:18 +08:00
虽然这些都能在根模块中做,但特性模块可以帮助我们把应用切分成具有特定关注点和目标的不同区域。
2016-07-12 18:14:13 -07:00
A feature module collaborates with the root module and with other modules
through the services it provides and
the components, directives, and pipes that it chooses to share.
2016-08-27 07:57:18 +08:00
特性模块通过自己提供的服务和它决定对外共享的那些组件、指令、管道来与根模块等其它模块协同工作。
2016-07-12 18:14:13 -07:00
In the next section, we carve the contact functionality out of the root module
and into a dedicated feature module.
2016-08-27 07:57:18 +08:00
在下一节,我们从根模块中把与联系人有关的功能切分到一个专门的特性模块中。
2016-07-12 18:14:13 -07:00
<a id="contact-module-v1"></a>
2016-07-12 18:14:13 -07:00
### Make _Contact_ a feature module
2016-08-27 07:57:18 +08:00
### 把_联系人_做成特性模块
2016-07-12 18:14:13 -07:00
It's easy to refactor the contact material into a contact feature module.
2016-08-27 07:57:18 +08:00
把与联系人有关的这些编程元素重构到“联系人”特性模块中很简单。
2016-07-12 18:14:13 -07:00
1. Create the `ContactModule` in the `app/contact` folder.
2016-08-27 07:57:18 +08:00
1. 在`app/contact`目录下创建`ContactModule`。
2016-07-12 18:14:13 -07:00
1. Move the contact material from `AppModule` to `ContactModule`.
2016-08-27 07:57:18 +08:00
1. 把联系人相关的编程元素从`AppModule`移到`ContactModule`中。
2016-07-12 18:14:13 -07:00
1. Replace the imported `BrowserModule` with `CommonModule`.
2016-08-27 07:57:18 +08:00
1. 把导入`BrowserModule`改为导入`CommonModule`。
2016-07-12 18:14:13 -07:00
1. Import the `ContactModule` into the `AppModule`.
2016-08-27 07:57:18 +08:00
1. 在`AppModule`中导入`ContactModule`。
2016-07-12 18:14:13 -07:00
`AppModule` is the only _existing_ class that changes. But we do add one new file.
2016-08-27 07:57:18 +08:00
改完之后`AppModule`只剩下了这个类本身,不过我们还会添加一个新文件。
2016-07-12 18:14:13 -07:00
### Add the _ContactModule_
2016-08-27 07:57:18 +08:00
### 添加_ContactModule_
2016-07-12 18:14:13 -07:00
Here's the new `ContactModule`
2016-08-27 07:57:18 +08:00
下面是新的`ContactModule`
2016-07-12 18:14:13 -07:00
+makeExample('ngmodule/ts/app/contact/contact.module.2.ts', '', 'app/contact/contact.module.ts')
2016-08-27 07:57:18 +08:00
2016-07-12 18:14:13 -07:00
:marked
We copy from `AppModule` the contact-related import statements and the `@NgModule` properties
that concern the contact and paste them in `ContactModule`.
2016-08-27 07:57:18 +08:00
我们从`AppModule`中把与联系人有关的import语句和`@NgModule`中与联系人有关的内容拷贝到`ContactModule`中。
2016-07-12 18:14:13 -07:00
We _import_ the `FormsModule` because the contact component needs it.
我们还_导入_了`FormsModule`,来满足“联系人”组件的需求。
2016-07-12 18:14:13 -07:00
.alert.is-important
:marked
Modules do not inherit access to the components, directives or pipes that are declared in other modules.
What `AppModule` imports is irrelevant to `ContactModule` and vice versa.
Before `ContactComponent` can bind with `[(ngModel)]`, its `ContactModule` must import `FormsModule`.
当前模块不会继承其它模块中对组件、指令或管道的访问权。
`AppModule`中的imports与`ContatModule`的imports互不相干。
如果`ContactComponent`要绑定到`[(ngModel)]`,它所在的`ContactModule`也同样得导入`FormsModule`。
2016-07-12 18:14:13 -07:00
:marked
We also replaced `BrowserModule` by `CommonModule` for reasons explained in
[an FAQ](../cookbook/ngmodule-faq.html#q-browser-vs-common-module).
我们还用`CommonModule`替换了`BrowserModule`,其中缘由参见[这条FAQ](../cookbook/ngmodule-faq.html#q-browser-vs-common-module)。
2016-07-12 18:14:13 -07:00
We _declare_ the contact component, directive, and pipe in the module `declarations`.
我们在该模块的`declarations`中*声明*了“联系人”的组件、指令和管道。
2016-07-12 18:14:13 -07:00
We _export_ the `ContactComponent` so
other modules that import the `ContactModule` can include it in their component templates.
我们*导出*了`ContactComponent`,以便其它导入了`ContactModule`的模块可以在其组件模板中使用`ContactModule`组件。
2016-07-12 18:14:13 -07:00
All other declared contact classes are private by default.
The `AwesomePipe` and `HighlightDirective` are hidden from the rest of the application.
The `HighlightDirective` can no longer color the `AppComponent` title text.
“联系人”中声明的所有其它类默认都是私有的。
`AwesomePipe`和`HighlightDirective`对应用的其它部分则是不可见的。
所以`HighlightDirective`不能把`AppComponent`的标题文字染色。
2016-07-12 18:14:13 -07:00
:marked
### Refactor the _AppModule_
### 重构*AppModule*
Return to the `AppModule` and remove everything specific to the contact feature set.
返回`AppModule`,并移除所有与联系人有关的内容。
Delete the contact import statements.
Delete the contact declarations and contact providers.
Remove the `FormsModule` from the `imports` list (`AppComponent` doesn't need it).
Leave only the classes required at the application root level.
删除属于联系人的`import`语句。
删除联系人的`declarations`和`providers`。
从`imports`列表中移除`FormsModule``AppComponent`并不需要它)。
只保留本应用的根一级需要的那些类。
Then import the `ContactModule` so the app can continue to display the exported `ContactComponent`.
然后,导入`ContactModule`,以便应用仍然可以显示从这里导出的`ContactComponent`。
Here's the refactored version of the `AppModule` side-by-side with the previous version.
下面是`AppModule`重构完的版本与之前版本的一对一对比。
2016-07-12 18:14:13 -07:00
+makeTabs(
`ngmodule/ts/app/app.module.2.ts,
ngmodule/ts/app/app.module.1b.ts`,
'',
`app/app.module.ts (v2),
app/app.module.ts (v1)`)
:marked
### Improvements
### 增强功能
2016-07-12 18:14:13 -07:00
:marked
There's a lot to like in the revised `AppModule`
修改后的`AppModule`中还有很多类似的修改
2016-07-12 18:14:13 -07:00
* It does not change as the _Contact_ domain grows.
* 它不会再随着_联系人_的领域扩张而修改。
2016-07-12 18:14:13 -07:00
* It only changes when we add new modules.
* 只有当添加新模块时才需要修改它。
2016-07-12 18:14:13 -07:00
* It's simpler:
* 它也变得简单了:
2016-07-12 18:14:13 -07:00
* Fewer import statements
* 更少的`import`语句
2016-07-12 18:14:13 -07:00
* No `FormsModule` import
* 不再导入`FormsModule`
2016-07-12 18:14:13 -07:00
* No contact-specific declarations
* 没有与联系人有关的声明
2016-07-12 18:14:13 -07:00
* No `ContactService` provider
* 没有`ContactService`提供商
2016-07-12 18:14:13 -07:00
* No `HighlightDirective` conflict
* 没有`HighlightDirective`冲突
2016-07-12 18:14:13 -07:00
Try this `ContactModule` version of the sample.
试试范例的`ContactModule`版。
<live-example embedded plnkr="contact.2" img="devguide/ngmodule/contact-2-plunker.png">Try the live example.</live-example>
<live-example embedded plnkr="contact.2" img="devguide/ngmodule/contact-2-plunker.png">试试在线例子</live-example>
2016-07-12 18:14:13 -07:00
a#lazy-load
.l-main-section
:marked
## Lazy loading modules with the Router
## 用路由器实现延迟Lazy加载
2016-07-12 18:14:13 -07:00
The Heroic Staffing Agency sample app has evolved.
It has two more modules, one for managing the heroes-on-staff and another for matching crises to the heroes.
Both modules are in the early stages of development.
Their specifics aren't important to the story and we won't discuss every line of code.
“职介中心”这个例子应用继续成长。
它又增加了两个模块,一个用来管理雇佣的英雄,另一个用来匹配英雄与危机。
这两个模块都还处于前期开发阶段。
它们对于整个故事来说无关紧要,这里我们就不逐行讨论了。
2016-07-12 18:14:13 -07:00
.l-sub-section
:marked
Examine and download the complete source for this version from the
<live-example plnkr="pre-shared.3" img="devguide/ngmodule/v3-plunker.png">live example.</live-example>
<live-example plnkr="pre-shared.3" img="devguide/ngmodule/v3-plunker.png">在线例子</live-example>
试用并下载当前版本的完整代码。
2016-07-12 18:14:13 -07:00
:marked
Some facets of the current application merit discussion.
当前应用中还有一些方面值得深入探讨。
2016-07-12 18:14:13 -07:00
* The app has three feature modules: Contact, Hero, and Crisis.
* 该应用有三个特性模块联系人Contact、英雄Hero和危机Crisis
2016-07-12 18:14:13 -07:00
* The Angular router helps users navigate among these modules.
* Angular路由器帮助用户在这些模块之间导航。
2016-07-12 18:14:13 -07:00
* The `ContactComponent` is the default destination when the app starts.
* `ContactComponent`组件是应用启动时的默认页。
2016-07-12 18:14:13 -07:00
* The `ContactModule` continues to be "eagerly" loaded when the application starts.
* `ContactModule`仍然会在应用启动时被立即加载。
2016-07-12 18:14:13 -07:00
* `HeroModule` and the `CrisisModule` are lazy loaded.
* `HeroModule`和`CrisisModule`会被延迟加载。
2016-07-12 18:14:13 -07:00
<a id="app-component-template"></a>
2016-07-12 18:14:13 -07:00
Let's start at the top with the new `AppComponent` template:
a title, three links, and a `<router-outlet>`.
我们从这个`AppComponent`新模板的顶部看起:
一个标题、三个链接和一个`<router-outlet>`。
2016-07-12 18:14:13 -07:00
+makeExample('ngmodule/ts/app/app.component.3.ts', 'template', 'app/app.component.ts (v3 - Template)')(format='.')
2016-07-12 18:14:13 -07:00
:marked
The `<app-contact>` element is gone; we're routing to the _Contact_ page now.
`<app-contact>`元素不见了,改成了路由到*联系人*页。
2016-07-12 18:14:13 -07:00
The `AppModule` has changed modestly:
`AppModule`被改成了这样:
2016-07-12 18:14:13 -07:00
+makeExample('ngmodule/ts/app/app.module.3.ts', '', 'app/app.module.ts (v3)')
2016-07-12 18:14:13 -07:00
.l-sub-section
:marked
Some file names bear a `.3` extension indicating
a difference with prior or future versions.
We'll explain differences that matter in due course.
有些文件名带有`.3`扩展名,用以和以前/以后的版本区分开。
我们会在适当的时机解释它们的差异。
2016-07-12 18:14:13 -07:00
:marked
The module still imports `ContactModule` so that its routes and components are mounted when the app starts.
该模块仍然要导入`ContactModule`模块,以便在应用启动时加载它的路由和组件。
2016-07-12 18:14:13 -07:00
The module does _not_ import `HeroModule` or `CrisisModule`.
They'll be fetched and mounted asynchronously when the user navigates to one of their routes.
该模块不用导入`HeroModule`或`CrisisModule`。
它们将在用户导航到其中的某个路由时,被异步获取并加载。
2016-07-12 18:14:13 -07:00
The significant change from version 2 is the addition of a ***routing*** object to the `imports`.
The routing object, which provides a configured `Router` service, is defined in the `app.routing.ts` file.
与第二版相比,最值得注意的修改是`imports`中那个额外的***routing***对象。
`routing`对象提供了一个配置好的`Router`服务,它是在`app.routing.ts`文件中定义的。
2016-07-12 18:14:13 -07:00
### App routing
### 应用路由
2016-07-12 18:14:13 -07:00
+makeExample('ngmodule/ts/app/app.routing.ts', '', 'app/app.routing.ts')(format='.')
2016-07-12 18:14:13 -07:00
:marked
The router is the subject of [its own page](router.html) so we'll skip lightly over the details and
2016-07-12 18:14:13 -07:00
concentrate on the intersection of Angular modules and routing.
路由器有[一个专门的页面](router.html)做了深入讲解所以这里我们跳过细节而是专注于它和Angular模块的协作。
2016-07-12 18:14:13 -07:00
This file defines three routes.
该文件定义了三个路由。
2016-07-12 18:14:13 -07:00
The first redirects the empty URL (e.g., `http://host.com/`)
to another route whose path is `contact` (e.g., `http://host.com/contact`).
第一个路由把空白URL比如`http://host.com/`)重定向到了另一个路径为`contact`的路由(比如:`http://host.com/contact`)。
2016-07-12 18:14:13 -07:00
The `contact` route isn't defined here.
It's defined in the _Contact_ feature's _own_ routing file, `contact.routing.ts`.
It's standard practice for feature modules with routing components to define their own routes.
We'll get to that file in a moment.
The remaining two routes use lazy loading syntax to tell the router where to find the modules:
+makeExample('ngmodule/ts/app/app.routing.ts', 'lazy-routes')(format='.')
.l-sub-section
:marked
A lazy loaded module location is a _string_, not a _type_.
In this app, the string identifies both the module _file_ and the module _class_,
the latter separated from the former by a `#`.
2016-07-12 18:14:13 -07:00
:marked
### RouterModule.forRoot
The last line calls the `forRoot` static class method of the `RouterModule`, passing in the configuration.
+makeExample('ngmodule/ts/app/app.routing.ts', 'forRoot')(format='.')
:marked
The returned `routing` object is a `ModuleWithProviders` containing both the `RouterModule` directives
and the Dependency Injection providers that produce a configured `Router`.
This `routing` object is intended for the app _root_ module _only_.
.alert.is-critical
:marked
Never call `RouterModule.forRoot` in a feature module.
:marked
Back in the root `AppModule`, we add this `routing` object to its `imports` list,
and the app is ready to navigate.
+makeExample('ngmodule/ts/app/app.module.3.ts', 'imports', 'app/app.module.ts (imports)')(format='.')
:marked
### Routing to a feature module
The `app/contact` folder holds a new file, `contact.routing.ts`.
It defines the `contact` route we mentioned a bit earlier and also creates a `routing` object like so:
+makeExample('ngmodule/ts/app/contact/contact.routing.ts', 'routing', 'app/contact/contact.routing.ts (routing)')(format='.')
:marked
This time we pass the route list to the `forChild` method of the `RouterModule`.
It produces a different kind of object intended for feature modules.
.alert.is-important
:marked
Always call `RouterModule.forChild` in a feature module.
.alert.is-helpful
:marked
**_forRoot_** and **_forChild_** are conventional names for methods that
deliver different `import` values to root and feature modules.
Angular doesn't recognize them but Angular developers do.
[Follow this convention](../cookbook/ngmodule-faq.html#q-for-root) if you write a similar module
that has both shared [_declarables_](../cookbook/ngmodule-faq.html#q-declarable) and services.
2016-07-12 18:14:13 -07:00
:marked
`ContactModule` has changed in two small but important details
+makeTabs(
`ngmodule/ts/app/contact/contact.module.3.ts,
ngmodule/ts/app/contact/contact.module.2.ts`,
'class, class',
`app/contact/contact.module.3.ts,
app/contact/contact.module.2.ts`)
:marked
1. It imports the `routing` object from `contact.routing.ts`
1. It no longer exports `ContactComponent`
Now that we navigate to `ContactComponent` with the router there's no reason to make it public.
Nor does it need a selector.
No template will ever again reference this `ContactComponent`.
It's gone from the [_AppComponent_ template](#app-component-template).
a#hero-module
:marked
### Lazy loaded routing to a module
The lazy loaded `HeroModule` and `CrisisModule` follow the same principles as any feature module.
They don't look different from the eagerly loaded `ContactModule`.
The `HeroModule` is a bit more complex than the `CrisisModule` which makes it
a more interesting and useful example. Here's its file structure:
.filetree
.file hero
.children
.file hero-detail.component.ts
.file hero-list.component.ts
.file hero.component.ts
.file hero.module.ts
.file hero.routing.ts
.file hero.service.ts
.file highlight.directive.ts
:marked
This is the child routing scenario familiar to readers of the [Router](router.html#child-routing-component) page.
2016-07-12 18:14:13 -07:00
The `HeroComponent` is the feature's top component and routing host.
Its template has a `<router-outlet>` that displays either a list of heroes (`HeroList`)
or an editor of a selected hero (`HeroDetail`).
Both components delegate to the `HeroService` to fetch and save data.
There's yet _another_ `HighlightDirective` that colors elements in yet a different shade.
We should [do something](#shared-module "Shared modules") about the repetition and inconsistencies.
We endure for now.
The `HeroModule` is a feature module like any other.
+makeExample('ngmodule/ts/app/hero/hero.module.3.ts', 'class', 'app/hero/hero.module.ts (class)')(format='.')
:marked
It imports the `FormsModule` because the `HeroDetailComponent` template binds with `[(ngModel)]`.
It imports a `routing` object from `hero.routing.ts` just as `ContactModule` and `CrisisModule` do.
2016-07-12 18:14:13 -07:00
The `CrisisModule` is much the same. There's nothing more to say that's new.
<live-example embedded plnkr="pre-shared.3" img="devguide/ngmodule/v3-plunker.png">Try the live example.</live-example>
2016-07-12 18:14:13 -07:00
a#shared-module
.l-main-section
:marked
## Shared modules
The app is shaping up.
One thing we don't like is carrying three different versions of the `HighlightDirective`.
And there's a bunch of other stuff cluttering the app folder level that could be tucked away.
Let's add a `SharedModule` to hold the common components, directives, and pipes
2016-07-12 18:14:13 -07:00
and share them with the modules that need them.
* create an `app/shared` folder
* move the `AwesomePipe` and `HighlightDirective` from `app/contact` to `app/shared`.
* delete the `HighlightDirective` classes from `app/` and `app/hero`
* create a `SharedModule` class to own the shared material
* update other feature modules to import `SharedModule`
2016-07-12 18:14:13 -07:00
Most of this is familiar blocking and tackling. Here is the `SharedModule`
2016-07-12 18:14:13 -07:00
+makeExample('ngmodule/ts/app/shared/shared.module.ts', '', 'app/app/shared/shared.module.ts')
:marked
Some highlights
* It imports the `CommonModule` because its component needs common directives.
* It declares and exports the utility pipe, directive, and component classes as expected.
* It re-exports the `CommonModule` and `FormsModule`
#### Re-exporting other modules
2016-07-12 18:14:13 -07:00
While reviewing our application, we noticed that many components requiring `SharedModule` directives
also use `NgIf` and `NgFor` from `CommonModule`
and bind to component properties with `[(ngModel)]`, a directive in the `FormsModule`.
Modules that declare these components would have to import `CommonModule`, `FormsModule` and `SharedModule`.
2016-07-12 18:14:13 -07:00
We can reduce the repetition by having `SharedModule` re-export `CommonModule` and `FormsModule`
so that importers of `SharedModule` get `CommonModule` and `FormsModule` _for free_.
As it happens, the components declared by `SharedModule` itself don't bind with `[(ngModel)]`.
Technically, there is no need for `SharedModule` to import `FormsModule`.
`SharedModule` can still export `FormsModule` without listing it among its `imports`.
2016-07-12 18:14:13 -07:00
### Why _TitleComponent_ isn't shared
2016-07-12 18:14:13 -07:00
`SharedModule` exists to make commonly used components, directives and pipes available
for use in the templates of components in _many_ other modules.
2016-07-12 18:14:13 -07:00
The `TitleComponent` is used _only once_ by the `AppComponent`.
There's no point in sharing it.
2016-07-12 18:14:13 -07:00
<a id="no-shared-module-providers"></a>
### Why _UserService_ isn't shared
2016-07-12 18:14:13 -07:00
While many components share the same service _instances_,
they rely on Angular dependency injection to do this kind of sharing, not the module system.
Several components of our sample inject the `UserService`.
There should be _only one_ instance of the `UserService` in the entire application
and _only one_ provider of it.
2016-07-12 18:14:13 -07:00
`UserService` is an application-wide singleton.
We don't want each module to have its own separate instance.
Yet there is [a real danger](../cookbook/ngmodule-faq.html#q-why-it-is-bad) of that happening
if the `SharedModule` provides the `UserService`.
2016-07-12 18:14:13 -07:00
.alert.is-critical
2016-07-12 18:14:13 -07:00
:marked
Do **not** specify app-wide singleton `providers` in a shared module.
A lazy loaded module that imports that shared module will make its own copy of the service.
2016-07-12 18:14:13 -07:00
a#core-module
.l-main-section
2016-07-12 18:14:13 -07:00
:marked
## The Core module
At the moment, our root folder is cluttered with the `UserService`
and the `TitleComponent` that only appears in the root `AppComponent`.
We did not include them in the `SharedModule` for reasons just explained.
2016-07-12 18:14:13 -07:00
Instead, we'll gather them in a single `CoreModule` that we **import _once_ when the app starts**
and _never import anywhere else_.
2016-07-12 18:14:13 -07:00
**Steps:**
* create an `app/core` folder
* move the `UserService` and `TitleComponent` from `app/` to `app/core`
* create a `CoreModule` class to own the core material
* update the `AppRoot` module to import `CoreModule`
Again, most of this is familiar blocking and tackling. The interesting part is the `CoreModule`
+makeExample('ngmodule/ts/app/core/core.module.ts', 'v4', 'app/app/core/core.module.ts')
.l-sub-section
2016-07-12 18:14:13 -07:00
:marked
We're importing some extra symbols from the Angular core library that we're not using yet.
They'll become relevant later in this page.
:marked
The `@NgModule` metadata should be familiar.
We declare the `TitleComponent` because this module _owns_ it and we export it
because `AppComponent` (which is in `AppModule`) displays the title in its template.
`TitleComponent` needs the Angular `NgIf` directive that we import from `CommonModule`.
`CoreModule` _provides_ the `UserService`. Angular registers that provider with the app root injector,
making a singleton instance of the `UserService` available to any component that needs it,
whether that component is eagerly or lazily loaded.
2016-07-12 18:14:13 -07:00
.l-sub-section
:marked
#### Why bother?
This scenario is clearly contrived.
The app is too small to worry about a single service file and a tiny, one-time component.
2016-07-12 18:14:13 -07:00
A `TitleComponent` sitting in the root folder isn't bothering anyone.
The root `AppModule` can register the `UserService` itself,
as it does currently, even if we decide to relocate the `UserService` file to the `app/core` folder.
Real world apps have more to worry about.
They can have several single-use components (e.g., spinners, message toasts, and modal dialogs)
that appear only in the `AppComponent` template.
We don't import them elsewhere so they're not _shared_ in that sense.
Yet they're too big and messy to leave loose in the root folder.
2016-07-12 18:14:13 -07:00
Apps often have many singleton services like this sample's `UserService`.
Each must be registered _exactly once_, in the app root injector, when the application starts.
2016-07-12 18:14:13 -07:00
While many Components inject such services in their constructors &mdash;
and therefore require JavaScript `import` statements to import their symbols &mdash;
no other component or module should define or re-create the services themselves.
Their _providers_ are not shared.
2016-07-12 18:14:13 -07:00
We recommend collecting such single-use classes and hiding their gory details inside a `CoreModule`.
A simplified root `AppModule` imports `CoreModule` in its capacity as orchestrator of the application as a whole.
.l-main-section
2016-07-12 18:14:13 -07:00
:marked
## Cleanup
Having refactored to a `CoreModule` and a `SharedModule`, it's time to cleanup the other modules.
2016-07-12 18:14:13 -07:00
### A trimmer _AppModule_
2016-07-12 18:14:13 -07:00
Here is the updated `AppModule` paired with version 3 for comparison:
+makeTabs(
`ngmodule/ts/app/app.module.ts,
ngmodule/ts/app/app.module.3.ts`,
'v4,',
`app/app.module.ts (v4),
2016-07-12 18:14:13 -07:00
app/app.module.ts (v3)`)
:marked
Notice that `AppModule` is ...
* a little smaller because many `app/root` classes have moved to other modules.
* stable because we'll add future components and providers to other modules, not this one.
* delegating to imported modules rather than doing work.
* focused on its main task, orchestrating the app as a whole.
2016-07-12 18:14:13 -07:00
### A trimmer _ContactModule_
Here is the new `ContactModule` paired with the prior version:
2016-07-12 18:14:13 -07:00
+makeTabs(
`ngmodule/ts/app/contact/contact.module.ts,
ngmodule/ts/app/contact/contact.module.3.ts`,
'',
`app/contact/contact.module.ts (v4),
2016-07-12 18:14:13 -07:00
app/contact/contact.module.ts (v3)`)
:marked
Notice that
* The `AwesomePipe` and `HighlightDirective` are gone.
* The imports include `SharedModule` instead of `CommonModule` and `FormsModule`
* This new version is leaner and cleaner.
2016-07-12 18:14:13 -07:00
.l-hr
a#core-for-root
2016-07-12 18:14:13 -07:00
.l-main-section
:marked
## Configure core services with _CoreModule.forRoot_
2016-07-12 18:14:13 -07:00
A module that adds providers to the application can offer a facility for configuring those providers as well.
2016-07-12 18:14:13 -07:00
By convention, the **_forRoot_** static method both provides and configures services at the same time.
It takes a service configuration object and returns a
[ModuleWithProviders](../api/core/index/ModuleWithProviders-interface.html) which is
a simple object with two properties:
* `ngModule` - the `CoreModule` class
* `providers` - the configured providers
2016-07-12 18:14:13 -07:00
The root `AppModule` imports the `CoreModule` and adds the `providers` to the `AppModule` providers.
.l-sub-section
2016-07-12 18:14:13 -07:00
:marked
More precisely, Angular accumulates all imported providers _before_ appending the items listed in `@NgModule.providers`.
This sequence ensures that whatever we add explicitly to the `AppModule` providers takes precedence
over the providers of imported modules.
2016-07-12 18:14:13 -07:00
:marked
Let's add a `CoreModule.forRoot` method that configures the core `UserService`.
2016-07-12 18:14:13 -07:00
We've extended the core `UserService` with an optional, injected `UserServiceConfig`.
If a `UserServiceConfig` exists, the `UserService` sets the user name from that config.
+makeExample('ngmodule/ts/app/core/user.service.ts', 'ctor', 'app/core/user.service.ts (constructor)')(format='.')
2016-07-12 18:14:13 -07:00
:marked
Here's `CoreModule.forRoot` that takes a `UserServiceConfig` object:
+makeExample('ngmodule/ts/app/core/core.module.ts', 'for-root', 'app/core/core.module.ts (forRoot)')(format='.')
2016-07-12 18:14:13 -07:00
:marked
Lastly, we call it _within the_ `imports` _list_ of the `AppModule`.
+makeExample('ngmodule/ts/app/app.module.ts', 'import-for-root', 'app//app.module.ts (imports)')(format='.')
2016-07-12 18:14:13 -07:00
:marked
The app displays "Miss Marple" as the user instead of the default "Sherlock Holmes".
2016-07-12 18:14:13 -07:00
.alert.is-important
2016-07-12 18:14:13 -07:00
:marked
Call `forRoot` only in the root application module, `AppModule`.
Calling it in any other module, particularly in a lazy loaded module,
is contrary to the intent and is likely to produce a runtime error.
2016-07-12 18:14:13 -07:00
Remember to _import_ the result; don't add it to any other `@NgModule` list.
2016-07-12 18:14:13 -07:00
.l-hr
a#prevent-reimport
2016-07-12 18:14:13 -07:00
.l-main-section
:marked
## Prevent reimport of the _CoreModule_
2016-07-12 18:14:13 -07:00
Only the root `AppModule` should import the `CoreModule`.
[Bad things happen](../cookbook/ngmodule-faq.html#q-why-it-is-bad) if a lazy loaded module imports it.
2016-07-12 18:14:13 -07:00
We could _hope_ that no developer makes that mistake.
Or we can guard against it and fail fast by adding the following `CoreModule` constructor.
+makeExample('ngmodule/ts/app/core/core.module.ts', 'ctor')(format='.')
2016-07-12 18:14:13 -07:00
:marked
The constructor tells Angular to inject the `CoreModule` into itself.
That seems dangerously circular.
2016-07-12 18:14:13 -07:00
The injection _would be circular_ if Angular looked for `CoreModule` in the _current_ injector.
The `@SkipSelf` decorator means "_look for_ `CoreModule` _in an ancestor injector, above me in the injector hierarchy._"
2016-07-12 18:14:13 -07:00
If the constructor executes as intended in the `AppModule`,
there is no ancestor injector that could provide an instance of `CoreModule`.
The injector should give up.
2016-07-12 18:14:13 -07:00
By default the injector throws an error when it can't find a requested provider.
The `@Optional` decorator means not finding the service is OK.
The injector returns `null`, the `parentModule` parameter is null,
and the constructor concludes uneventfully.
2016-07-12 18:14:13 -07:00
It's a different story if we improperly import `CoreModule` into a lazy loaded module such as `HeroModule` (try it).
2016-07-12 18:14:13 -07:00
Angular creates a lazy loaded module with its own injector, a _child_ of the root injector.
`@SkipSelf` causes Angular to look for a `CoreModule` in the parent injector which this time is the root injector.
Of course it finds the instance imported by the root `AppModule`.
Now `parentModule` exists and the constructor throws the error.
2016-07-12 18:14:13 -07:00
:marked
### Conclusion
2016-07-12 18:14:13 -07:00
You made it! You can examine and download the complete source for this final version from the live example.
<live-example embedded img="devguide/ngmodule/final-plunker.png"></live-example>
2016-07-12 18:14:13 -07:00
### Frequently Asked Questions
2016-07-12 18:14:13 -07:00
Now that you understand Angular Modules, you may be interested
in the companion [Angular Module FAQs](../cookbook/ngmodule-faq.html "Angular Module FAQs") cookbook
with its ready answers to specific design and implementation questions.