docs: add library doc to guides in aio (#27581)

PR Close #27581
This commit is contained in:
Judy Bogart 2018-12-05 13:29:31 -08:00 committed by Andrew Kushnir
parent 133fe5e561
commit e082fc24b2
4 changed files with 386 additions and 0 deletions

View File

@ -0,0 +1,189 @@
# Creating Libraries
You can create and publish new libraries to extend Angular functionality. If you find that you need to solve the same problem in more than one app (or want to share your solution with other developers), you have a candidate for a library.
An simple example might be a button that sends users to your company website, that would be included in all apps that your company builds.
## Getting started
Use the Angular CLI to generate a new library skeleton with the following command:
<code-example format="." language="bash">
ng generate library my-lib
</code-example>
This creates the `projects/my-lib` folder in your workspace, which contains a component and a service inside an NgModule.
The workspace configuration file, `angular.json`, is updated with a project of type 'library'.
<code-example format="json">
"projects": {
...
"my-lib": {
"root": "projects/my-lib",
"sourceRoot": "projects/my-lib/src",
"projectType": "library",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
...
</code-example>
You can build, test, and lint the project with CLI commands:
<code-example format="." language="bash">
ng build my-lib
ng test my-lib
ng lint my-lib
</code-example>
Notice that the configured builder for the project is different from the default builder for app projects.
This builder, among other things, ensures that the library is always built with the [AoT compiler](guide/aot-compiler), without the need to specify the `--prod` flag.
To make library code reusable you must define a public API for it. This "user layer" defines what is available to consumers of your library. A user of your library should be able to access public functionality (such as NgModules, service providers and general utility functions) through a single import path.
The public API for your library is maintained in the `index.ts` file of your library folder.
Anything exported from this file is made public when your library is imported into an application.
Use an NgModule to expose services and components.
Your library should supply documentation (typically a README file) for installation and maintenance.
## Refactoring parts of an app into a library
To make your solution reusable, you need to adjust it so that it does not depend on app-specific code.
Here are some things to consider in migrating application functionality to a library.
* Declarations such as components and pipes should be designed as stateless, meaning they dont rely on or alter external variables. If you do rely on state, you need to evaluate every case and decide whether it is application state or state that the library would manage.
* Any observables that the components subscribe to internally should be cleaned up and disposed of during the lifecycle of those components.
* Components should expose their interactions through inputs for providing context, and outputs for communicating events to other components.
* Services should declare their own providers (rather than declaring providers in the NgModule or a component), so that they are *tree-shakable*. This allows the compiler to leave the service out of the bundle if it never gets injected into the application that imports the library. For more about this, see [Tree-shakable providers](guide/dependency-injection-providers#tree-shakable-providers).
* If you register global service providers or share providers across multiple NgModules, use the [`forRoot()` and `forChild()` patterns](guide/singleton-services) provided by the [RouterModule](api/router/RouterModule).
* Check all internal dependencies.
* For custom classes or interfaces used in components or service, check whether they depend on additional classes or interfaces that also need to be migrated.
* Similarly, if your library code depends on a service, that service needs to be migrated.
* If your library code or its templates depend on other libraries (such a Angular Material, for instance), you must configure your library with those dependencies.
## Reusable code and schematics
A library typically includes *reusable code* that defines components, services, and other Angular artifacts (pipes, directives, and so on) that you simply import into a project.
A library is packaged into an npm package for publishing and sharing, and this package can also include [schematics](guide/glossary#schematic) that provide instructions for generating or transforming code directly in your project, in the same way that the CLI creates a generic skeleton app with `ng generate component`.
A schematic that is combined with a library can, for example, provide the Angular CLI with the information it needs to generate a particular component defined in that library.
What you include in your library is determined by the kind of task you are trying to accomplish.
For example, if you want a dropdown with some canned data to show how to add it to your app, your library could define a schematic to create it.
For a component like a dropdown that would contain different passed-in values each time, you could provide it as a component in a shared library.
Suppose you want to read a configuration file and then generate a form based on that configuration.
If that form will need additional customization by the user, it might work best as a schematic.
However, if the forms will always be the same and not need much customization by developers, then you could create a dynamic component that takes the configuration and generates the form.
In general, the more complex the customization, the more useful the schematic approach.
## Integrating with the CLI
A library can include [schematics](guide/glossary#schematic) that allow it to integrate with the Angular CLI.
* Include an installation schematic so that `ng add` can add your library to a project.
* Include generation schematics in your library so that `ng generate` can scaffold your defined artifacts (components, services, tests, and so on) in a project.
* Include an update schematic so that `ng update` can update your librarys dependencies and provide migrations for breaking changes in new releases.
To learn more, see [SchematicsAn Introduction](https://blog.angular.io/schematics-an-introduction-dc1dfbc2a2b2).
## Publishing your library
Use the Angular CLI and the npm package manager to build and publish your library as an npm package.
Libraries are built in [AoT mode](guide/aot-compiler) by default, so you do not need to specify the `-prod` flag when building for publication.
<code-example format="." language="bash">
ng build my-lib
cd dist/my-lib
npm publish
</code-example>
If you've never published a package in npm before, you must create a user account. Read more in [Publishing npm Packages](https://docs.npmjs.com/getting-started/publishing-npm-packages).
## Linked libraries
While working on a published library, you can use [npm link](https://docs.npmjs.com/cli/link) to avoid reinstalling the library on every build.
The library must be rebuilt on every change.
When linking a library, make sure that the build step runs in watch mode, and that the library's `package.json` configuration points at the correct entry points.
For example, `main` should point at a JavaScript file, not a TypeScript file.
## Use TypeScript path mapping for peer dependencies
Angular libraries should list all `@angular/*` dependencies as peer dependencies.
This insures that when modules ask for Angular, they all get the exact same module.
If a library lists `@angular/core` in `dependencies` instead of `peerDependencies`, it might get a different Angular module instead, which would cause your application to break.
While developing a library, you must install all peer dependencies through `devDependencies` to ensure that the library compiles properly.
A linked library will then have its own set of Angular libraries that it uses for building, located in its `node_modules` folder.
However, this can cause problems while building or running your application.
To get around this problem you can use TypeScript path mapping to tell TypeScript that it should load some modules from a specific location.
List all the peer dependencies that your library uses in the TypeScript configuration file `./tsconfig.json`, and point them at the local copy in the app's `node_modules` folder.
```
{
"compilerOptions": {
// ...
// paths are relative to `baseUrl` path.
"paths": {
"@angular/*": [
"../node_modules/@angular/*"
]
}
}
}
```
This mapping ensures that your library always loads the local copies of the modules it needs.
## Using your own library in apps
You don't have to publish your library to the npm package manager in order to use it in your own apps, but you do have to build it first.
To use your own library in an app:
* Build the library. You cannot use a library before it is built.
<code-example format="." language="bash">
ng build my-lib
</code-example>
* In your apps, import from the library by name:
```
import { my-export } from 'my-lib';
```
### Building and rebuilding your library
The build step is important if you haven't published your library as an npm package and then installed the package back into your app from npm.
For instance, if you clone your git repository and run `npm install`, your editor will show the `my-lib` imports as missing if you haven't yet built your library.
<div class="alert is-helpful">
When you import something from a library in an Angular app, Angular looks for a mapping between the library name and a location on disk.
When you install a library package, the mapping is in the `node_modules` folder. When you build your own library, it has to find the mapping in your `tsconfig` paths.
Generating a library with the Angular CLI automatically adds its path to the `tsconfig` file.
The Angular CLI uses the `tsconfig` paths to tell the build system where to find the library.
</div>
If you find that changes to your library are not reflected in your app, your app is probably using an old build of the library.
You can rebuild your library whenever you make changes to it, but this extra step takes time.
*Incremental builds* functionality improves the library-development experience.
Every time a file is changed a partial build is performed that emits the amended files.
Incremental builds can be run as a backround process in your dev environment. To take advantage of this feature add the `--watch` flag to the build command:
<code-example format="." language="bash">
ng build my-lib --watch
</code-example>

View File

@ -0,0 +1,30 @@
# Overview of Angular Libraries
Many applications need to solve the same general problems, such as presenting a unified user interface, presenting data, and allowing data entry.
Developers can create general solutions for particular domains that can be adapted for re-use in different apps.
Such a solution can be built as Angular *libraries* and these libraries can be published and shared as *npm packages*.
An Angular library is an Angular [project](guide/glossary#project) that differs from an app in that it cannot run on its own.
A library must be imported and used in an app.
Libraries extend Angular's base functionality. For example, to add [reactive forms](guide/reactive-forms) to an app, add the library package using `ng add @angular/forms`, then import the `ReactiveFormsModule` from the `@angular/forms` library in your application code.
Similarly, adding the [service worker](guide/service-worker-intro) library to an Angular application is one of the steps for turning an application into a [Progressive Web App](https://developers.google.com/web/progressive-web-apps/) (PWA).
[Angular Material](https://material.angular.io/) is an example of a large, general-purpose library that provides sophisticated, reusable, and adaptable UI components.
Any app developer can use these and other libraries that have been published as npm packages by the Angular team or by third parties. See [Using Published Libraries](guide/using-libraries).
## Creating libraries
If you have developed functionality that is suitable for reuse, you can create your own libraries.
These libraries can be used locally in your workspace, or you can publish them as [npm packages](guide/npm-packages) to share with other projects or other Angular developers.
These packages can be published to the npm registry, a private npm Enterprise registry, or a private package management system that supports npm packages.
See [Creating Libraries](guide/creating-libraries).
Whether you decide to package functionality as a library is an architectural decision, similar to deciding whether a piece of functionality is a component or a service, or deciding on the scope of a component.
Packaging functionality as a library forces the artifacts in the library to be decoupled from the application's business logic.
This can help to avoid various bad practices or architecture mistakes that can make it difficult to decouple and reuse code in the future.
Putting code into a separate library is more complex than simply putting everything in one app.
It requires more of an investment in time and thought for managing, maintaining, and updating the library.
This complexity can pay off, however, when the library is being used in multiple apps.

View File

@ -0,0 +1,146 @@
# Using Published Libraries
When building Angular applications you can take advantage of sophisticated first-party libraries, such as [Angular Material](https://material.angular.io/), as well as rich ecosystem of third-party libraries.
See the [Angular Resources](https://angular.io/resources) page for links to the most popular ones.
## Installing libraries
Libraries are published as [npm packages](guide/npm-packages), usually together with schematics that integrate them with the Angular CLI.
To integrate reusable library code into an application, you need to install the package and import the provided functionality where you will use it. For most published Angular libraries, you can use the Angular CLI `ng add <lib_name>` command.
The `ng add` command uses the npm package manager or [yarn](https://yarnpkg.com/) to install the library package, and invokes schematics that are included in the package to other scaffolding within the project code, such as adding import statements, fonts, themes, and so on.
A published library typically provides a README or other documentation on how to add that lib to your app.
For an example, see [Angular Material](https://material.angular.io/) docs.
### Library typings
Library packages often include typings in `.d.ts` files; see examples in `node_modules/@angular/material`. If your library's package does not include typings and your IDE complains, you may need to install the library's associated `@types/<lib_name>` package.
To configure a library that does not include typings in the same package, install the related `@types` package with npm.
TypeScript looks for types in the `node_modules/@types` folder by default, so you don't have to add each type package individually.
For example, suppose you have a library named `d3`:
<code-example format="." language="bash">
npm install d3 --save
npm install @types/d3 --save-dev
</code-example>
Types defined in the library need to be added to the TypeScript configuration for the project that uses it.
* Add the library to the "types" array in `src/tsconfig.app.json`.
```
"types":[
"d3"
]
```
If a library doesn't have typings available at `@types/`, you can still use it by manually adding typings for it.
To do this:
1. Create a `typings.d.ts` file in your `src/` folder. This file is automatically included as global type definition.
2. Add the following code in `src/typings.d.ts`.
```
declare module 'host' {
export interface Host {
protocol?: string;
hostname?: string;
pathname?: string;
}
export function parse(url: string, queryString?: string): Host;
}
```
3. In the component or file that uses the library, add the following code.
```
import * as host from 'host';
const parsedUrl = host.parse('https://angular.io');
console.log(parsedUrl.hostname);
```
You can define more typings as needed.
## Updating libraries
Libraries can be updated by their publishers, and also have their own dependencies which need to be kept current.
To check for updates to your installed libraries, use the [`ng update` command](cli/update).
Use `ng update <lib_name>` to update individual library versions. The Angular CLI checks the latest published release of the library, and if the latest version is newer than your installed version, downloads it and updates your `package.json` to match the latest version.
When you update Angular to a new version, you need to make sure that any libraries you are using are current. If libraries have interdependencies, you might have to update them in a particular order.
See the [Angular Update Guide](https://update.angular.io/) for help.
## Adding a library to the runtime global scope
Legacy JavaScript libraries that are not imported into an app can be added to the runtime global scope and loaded as if they were in a script tag.
Configure the CLI to do this at build time using the "scripts" and "styles" options of the build target in the [CLI configuration file](guide/workspace-config), `angular.json`.
For example, to use the [Bootstrap 4](https://getbootstrap.com/docs/4.0/getting-started/introduction/) library, first install the library and its dependencies using the npm package manager:
<code-example format="." language="bash">
npm install jquery --save
npm install popper.js --save
npm install bootstrap --save
</code-example>
In the `angular.json` configuration file, add the associated script files to the "scripts" array:
```
"scripts": [
"node_modules/jquery/dist/jquery.slim.js",
"node_modules/popper.js/dist/umd/popper.js",
"node_modules/bootstrap/dist/js/bootstrap.js"
],
```
Add the Bootstrap CSS file to the "styles" array:
```
"styles": [
"node_modules/bootstrap/dist/css/bootstrap.css",
"src/styles.css"
],
```
Run or restart `ng serve` to see Bootstrap 4 working in your app.
### Using runtime-global libraries inside your app
Once you import a library using the "scripts" array, you should **not** import it using an import statement in your TypeScript code (such as `import * as $ from 'jquery';`).
If you do, you'll end up with two different copies of the library: one imported as a global library, and one imported as a module.
This is especially bad for libraries with plugins, like JQuery, because each copy will have different plugins.
Instead, download typings for your library (`npm install @types/jquery`) and follow the library installation steps. This gives you access to the global variables exposed by that library.
### Defining typings for runtime-global libraries
If the global library you need to use does not have global typings, you can declare them manually as `any` in `src/typings.d.ts`. For example:
```
declare var libraryName: any;
```
Some scripts extend other libraries; for instance with JQuery plugins:
```
$('.test').myPlugin();
```
In this case, the installed `@types/jquery` doesn't include `myPlugin`, so you need to add an interface in `src/typings.d.ts`. For example:
```
interface JQuery {
myPlugin(options?: any): any;
}
```
If don't add the interface for the script-defined extension, your IDE shows an error:
```
[TS][Error] Property 'myPlugin' does not exist on type 'JQuery'
```

View File

@ -494,6 +494,27 @@
"tooltip": "Learn how AngularJS concepts and techniques map to Angular."
}
]
},
{
"title": "Angular Libraries",
"tooltip": "Extending Angular with shared libraries.",
"children": [
{
"url": "guide/libraries",
"title": "Libraries Overview",
"tooltip": "Understand how and when to use or create libraries."
},
{
"url": "guide/using-libraries",
"title": "Using Published Libraries",
"tooltip": "Integrate published libraries into an app."
},
{
"url": "guide/creating-libraries",
"title": "Creating Libraries",
"tooltip": "Extend Angular by creating, publishing, and using your own libraries."
}
]
}
]
},