填寫這份《一分鐘調查》,幫我們(開發組)做得更好!去填寫Home

函式庫的原理圖

Schematics for libraries

當建立 Angular 函式庫時,你可以為同時為它打包進一組原理圖,並把它與 Angular CLI 整合在一起。藉助原理圖,使用者可以用 ng add 來安裝你這個函式庫的初始版本,可以用 ng generate 來建立你在函式庫中定義的一些工件,可以用 ng update 來調整他們的專案,以支援你在函式庫的新版本中引入的重大變更。

When you create an Angular library, you can provide and package it with schematics that integrate it with the Angular CLI. With your schematics, your users can use ng add to install an initial version of your library, ng generate to create artifacts defined in your library, and ng update to adjust their project for a new version of your library that introduces breaking changes.

這三種原理圖都可以作為你打包進函式庫中的集合的一部分。

All three types of schematics can be part of a collection that you package with your library.

下載函式庫的原理圖專案函式庫的原理圖專案以獲取一個已完成下列步驟的例子。

Download thelibrary schematics projectlibrary schematics projectfor a completed example of the steps below.

建立一個原理圖集合

Creating a schematics collection

要開始一個集合,你需要建立一些原理圖檔案。下列步驟說明了如何在不修改任何專案檔案的情況下新增初始支援。

To start a collection, you need to create the schematic files. The following steps show you how to add initial support without modifying any project files.

  1. 在函式庫的根資料夾中,建立一個 schematics/ 資料夾。

    In your library's root folder, create a schematics/ folder.

  2. schematics/ 資料夾中,為你的第一個原理圖建立一個 ng-add/ 資料夾。

    In the schematics/ folder, create an ng-add/ folder for your first schematic.

  3. schematics/ 資料夾的根級,建立一個 collection.json 檔案。

    At the root level of the schematics/ folder, create a collection.json file.

  4. 編輯 collection.json 檔案來定義你的集合的初始模式定義。

    Edit the collection.json file to define the initial schema for your collection.

{ "$schema": "../../../node_modules/@angular-devkit/schematics/collection-schema.json", "schematics": { "ng-add": { "description": "Add my library to the project.", "factory": "./ng-add/index#ngAdd" } } }
projects/my-lib/schematics/collection.json (Schematics Collection)
      
      {
  "$schema": "../../../node_modules/@angular-devkit/schematics/collection-schema.json",
  "schematics": {
    "ng-add": {
      "description": "Add my library to the project.",
      "factory": "./ng-add/index#ngAdd"
    }
  }
}
    
  • $schema 路徑是相對於 Angular Devkit 集合模式定義的。

    The $schema path is relative to the Angular Devkit collection schema.

  • schematics 物件描述了該集合中的命名原理圖。

    The schematics object describes the named schematics that are part of this collection.

  • 第一個條目是名為 ng-add 的原理圖。它包含了描述,並指向執行此原理圖時要呼叫的工廠函式。

    The first entry is for a schematic named ng-add. It contains the description, and points to the factory function that is called when your schematic is executed.

  1. 在這個函式庫專案的 package.json 檔案中,新增一個 “schematics” 的條目,裡面帶有你的模式定義檔案的路徑。當 Angular CLI 執行命令時,會根據這個條目在你的集合中查詢指定名字的原理圖。

    In your library project's package.json file, add a "schematics" entry with the path to your schema file. The Angular CLI uses this entry to find named schematics in your collection when it runs commands.

{ "name": "my-lib", "version": "0.0.1", "schematics": "./schematics/collection.json", }
projects/my-lib/package.json (Schematics Collection Reference)
      
      {
  "name": "my-lib",
  "version": "0.0.1",
  "schematics": "./schematics/collection.json",
}
    

你所建立的初始模式告訴 CLI 在哪裡可以找到支援 ng add 命令的原理圖。現在,你已準備好建立該原理圖了。

The initial schema that you have created tells the CLI where to find the schematic that supports the ng add command. Now you are ready to create that schematic.

提供安裝支援

Providing installation support

ng add 命令的原理圖可以增強使用者的初始安裝過程。可以按如下步驟定義這種原理圖。

A schematic for the ng add command can enhance the initial installation process for your users. The following steps will define this type of schematic.

  1. 進入/schematics/ng-add/ 目錄。

    Go to the/schematics/ng-add/ folder.

  2. 建立主檔案 index.ts

    Create the main file, index.ts.

  3. 開啟 index.ts 並新增原理圖工廠函式的原始碼。

    Open index.ts and add the source code for your schematic factory function.

import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics'; import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks'; // Just return the tree export function ngAdd(options: any): Rule { return (tree: Tree, context: SchematicContext) => { context.addTask(new NodePackageInstallTask()); return tree; }; }
projects/my-lib/schematics/ng-add/index.ts (ng-add Rule Factory)
      
      import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';

// Just return the tree
export function ngAdd(options: any): Rule {
  return (tree: Tree, context: SchematicContext) => {
    context.addTask(new NodePackageInstallTask());
    return tree;
  };
}
    

提供初始 ng add 支援所需的唯一步驟是使用 SchematicContext 來觸發安裝任務。該任務會藉助使用者首選的套件管理器將該函式庫新增到宿主專案的 package.json 配置檔案中,並將其安裝到該專案的 node_modules 目錄下。

The only step needed to provide initial ng add support is to trigger an installation task using the SchematicContext. The task uses the user's preferred package manager to add the library to the project's package.json configuration file, and install it in the project’s node_modules directory.

在這個例子中,該函式會接收當前的 Tree 並返回它而不作任何修改。如果需要,你也可以在安裝軟體包時進行額外的設定,例如產生檔案、更新配置、或者函式庫所需的任何其它初始設定。

In this example, the function receives the current Tree and returns it without any modifications. If you need to, you can do additional setup when your package is installed, such as generating files, updating configuration, or any other initial setup your library requires.

定義依賴型別

Define dependency type

如果該函式庫應該新增到 dependencies 中、devDepedencies 中,或者不用儲存到專案的 package.json 配置檔案中,請使用 ng-addsave 選項進行配置

Use the save option of ng-add to configure if the library should be added to the dependencies, the devDepedencies, or not saved at all in the project's package.json configuration file.

"ng-add": { "save": "devDependencies" }
projects/my-lib/package.json (ng-add Reference)
      
      "ng-add": {
  "save": "devDependencies"
}
    

可能的值有:

Possible values are:

  • false - 不把此包新增到 package.json

    false - Don't add the package to package.json

  • true - 把此包新增到 dependencies

    true - Add the package to the dependencies

  • "dependencies" - 把此包新增到 dependencies

    "dependencies" - Add the package to the dependencies

  • "devDependencies" - 把此包新增到 devDependencies

    "devDependencies" - Add the package to the devDependencies

建構你的原理圖

Building your schematics

要把你的原理圖和函式庫打包到一起,就必須把這個函式庫配置成單獨建構原理圖,然後再把它們新增到發佈套件中。你必須先建構函式庫再建構原理圖,這樣才能把它們放到正確的目錄下。

To bundle your schematics together with your library, you must configure the library to build the schematics separately, then add them to the bundle. You must build your schematics after you build your library, so they are placed in the correct directory.

  • 你的函式庫需要一個自訂的 Typescript 配置檔案,裡面帶有如何把原理圖編譯進函式庫的發佈版的一些指令。

    Your library needs a custom Typescript configuration file with instructions on how to compile your schematics into your distributed library.

  • 要把這些原理圖新增到函式庫的發佈套件中,就要把這些指令碼新增到該函式庫的 package.json 檔案中。

    To add the schematics to the library bundle, add scripts to the library's package.json file.

假設你在 Angular 工作區中有一個函式庫專案 my-lib。要想告訴函式庫如何建構原理圖,就要在產生的 tsconfig.lib.json 函式庫配置檔案旁新增一個 tsconfig.schematics.json 檔案。

Assume you have a library project my-lib in your Angular workspace. To tell the library how to build the schematics, add a tsconfig.schematics.json file next to the generated tsconfig.lib.json file that configures the library build.

  1. 編輯 tsconfig.schematics.json 檔案,新增如下內容。

    Edit the tsconfig.schematics.json file to add the following content.

{ "compilerOptions": { "baseUrl": ".", "lib": [ "es2018", "dom" ], "declaration": true, "module": "commonjs", "moduleResolution": "node", "noEmitOnError": true, "noFallthroughCasesInSwitch": true, "noImplicitAny": true, "noImplicitThis": true, "noUnusedParameters": true, "noUnusedLocals": true, "rootDir": "schematics", "outDir": "../../dist/my-lib/schematics", "skipDefaultLibCheck": true, "skipLibCheck": true, "sourceMap": true, "strictNullChecks": true, "target": "es6", "types": [ "jasmine", "node" ] }, "include": [ "schematics/**/*" ], "exclude": [ "schematics/*/files/**/*" ] }
projects/my-lib/tsconfig.schematics.json (TypeScript Config)
      
      {
  "compilerOptions": {
    "baseUrl": ".",
    "lib": [
      "es2018",
      "dom"
    ],
    "declaration": true,
    "module": "commonjs",
    "moduleResolution": "node",
    "noEmitOnError": true,
    "noFallthroughCasesInSwitch": true,
    "noImplicitAny": true,
    "noImplicitThis": true,
    "noUnusedParameters": true,
    "noUnusedLocals": true,
    "rootDir": "schematics",
    "outDir": "../../dist/my-lib/schematics",
    "skipDefaultLibCheck": true,
    "skipLibCheck": true,
    "sourceMap": true,
    "strictNullChecks": true,
    "target": "es6",
    "types": [
      "jasmine",
      "node"
    ]
  },
  "include": [
    "schematics/**/*"
  ],
  "exclude": [
    "schematics/*/files/**/*"
  ]
}
    
  • rootDir 指出在你的 schematics/ 資料夾中包含要編譯的輸入檔案。

    The rootDir specifies that your schematics/ folder contains the input files to be compiled.

  • outDir 對映到了函式庫的輸出目錄下。預設情況下,這是工作空間根目錄下的 dist/my-lib 資料夾。

    The outDir maps to the library's output folder. By default, this is the dist/my-lib folder at the root of your workspace.

  1. 要確保你的原理圖原始檔會被編譯進函式庫套件中,請把下列指令碼新增到函式庫專案的根資料夾(projects/my-lib)下的 package.json 檔案中。

    To make sure your schematics source files get compiled into the library bundle, add the following scripts to the package.json file in your library project's root folder (projects/my-lib).

{ "name": "my-lib", "version": "0.0.1", "scripts": { "build": "../../node_modules/.bin/tsc -p tsconfig.schematics.json", "copy:schemas": "cp --parents schematics/*/schema.json ../../dist/my-lib/", "copy:files": "cp --parents -p schematics/*/files/** ../../dist/my-lib/", "copy:collection": "cp schematics/collection.json ../../dist/my-lib/schematics/collection.json", "postbuild": "npm run copy:schemas && npm run copy:files && npm run copy:collection" }, "peerDependencies": { "@angular/common": "^7.2.0", "@angular/core": "^7.2.0" }, "schematics": "./schematics/collection.json", "ng-add": { "save": "devDependencies" } }
projects/my-lib/package.json (Build Scripts)
      
      {
  "name": "my-lib",
  "version": "0.0.1",
  "scripts": {
    "build": "../../node_modules/.bin/tsc -p tsconfig.schematics.json",
    "copy:schemas": "cp --parents schematics/*/schema.json ../../dist/my-lib/",
    "copy:files": "cp --parents -p schematics/*/files/** ../../dist/my-lib/",
    "copy:collection": "cp schematics/collection.json ../../dist/my-lib/schematics/collection.json",
    "postbuild": "npm run copy:schemas && npm run copy:files && npm run copy:collection"
  },
  "peerDependencies": {
    "@angular/common": "^7.2.0",
    "@angular/core": "^7.2.0"
  },
  "schematics": "./schematics/collection.json",
  "ng-add": {
    "save": "devDependencies"
  }
}
    
  • build 指令碼使用自訂的 tsconfig.schematics.json 檔案來編譯你的原理圖。

    The build script compiles your schematic using the custom tsconfig.schematics.json file.

  • copy:* 語句將已編譯的原理圖檔案複製到函式庫的輸出目錄下的正確位置,以保持目錄的結構。

    The copy:* statements copy compiled schematic files into the proper locations in the library output folder in order to preserve the file structure.

  • postbuild 指令碼會在 build 指令碼完成後複製原理圖檔案。

    The postbuild script copies the schematic files after the build script completes.

提供產生器支援

Providing generation support

你可以把一個命名原理圖新增到集合中,讓你的使用者可以使用 ng generate 命令來建立你在函式庫中定義的工件。

You can add a named schematic to your collection that lets your users use the ng generate command to create an artifact that is defined in your library.

我們假設你的函式庫定義了一項需要進行某些設定的服務 my-service。你希望使用者能夠用下面的 CLI 命令來產生它。

We'll assume that your library defines a service, my-service, that requires some setup. You want your users to be able to generate it using the following CLI command.

ng generate my-lib:my-service
      
      ng generate my-lib:my-service
    

首先,在 schematics 資料夾中新建一個子資料夾 my-service

To begin, create a new subfolder, my-service, in the schematics folder.

配置新的原理圖

Configure the new schematic

當你要把一個原理圖新增到集合中時,就必須在該集合的模式中指向它,並提供一些配置檔案來定義使用者可以傳給該命令的選項。

When you add a schematic to the collection, you have to point to it in the collection's schema, and provide configuration files to define options that a user can pass to the command.

  1. 編輯一下 schematics/collection.json 檔案,指向新的原理圖子資料夾,並附上一個指向模式檔案的指標,該檔案將會指定新原理圖的輸入。

    Edit the schematics/collection.json file to point to the new schematic subfolder, and include a pointer to a schema file that will specify inputs for the new schematic.

{ "$schema": "../../../node_modules/@angular-devkit/schematics/collection-schema.json", "schematics": { "ng-add": { "description": "Add my library to the project.", "factory": "./ng-add/index#ngAdd" }, "my-service": { "description": "Generate a service in the project.", "factory": "./my-service/index#myService", "schema": "./my-service/schema.json" } } }
projects/my-lib/schematics/collection.json (Schematics Collection)
      
      {
  "$schema": "../../../node_modules/@angular-devkit/schematics/collection-schema.json",
  "schematics": {
    "ng-add": {
      "description": "Add my library to the project.",
      "factory": "./ng-add/index#ngAdd"
    },
    "my-service": {
      "description": "Generate a service in the project.",
      "factory": "./my-service/index#myService",
      "schema": "./my-service/schema.json"
    }
  }
}
    
  1. 進入 <lib-root>/schematics/my-service/ 目錄。

    Go to the <lib-root>/schematics/my-service/ folder.

  2. 建立一個 schema.json 檔案並定義該原理圖的可用選項。

    Create a schema.json file and define the available options for the schematic.

{ "$schema": "http://json-schema.org/schema", "id": "SchematicsMyService", "title": "My Service Schema", "type": "object", "properties": { "name": { "description": "The name of the service.", "type": "string" }, "path": { "type": "string", "format": "path", "description": "The path to create the service.", "visible": false }, "project": { "type": "string", "description": "The name of the project.", "$default": { "$source": "projectName" } } }, "required": [ "name" ] }
projects/my-lib/schematics/my-service/schema.json (Schematic JSON Schema)
      
      {
  "$schema": "http://json-schema.org/schema",
  "id": "SchematicsMyService",
  "title": "My Service Schema",
  "type": "object",
  "properties": {
    "name": {
      "description": "The name of the service.",
      "type": "string"
    },
    "path": {
      "type": "string",
      "format": "path",
      "description": "The path to create the service.",
      "visible": false
    },
    "project": {
      "type": "string",
      "description": "The name of the project.",
      "$default": {
        "$source": "projectName"
      }
    }
   },
  "required": [
    "name"
  ]
}
    
  • id:這個模式定義在集合中的唯一 id。

    id: A unique id for the schema in the collection.

  • title:一個人類別可讀的模式描述。

    title: A human-readable description of the schema.

  • type:由這些屬性提供的型別描述符。

    type: A descriptor for the type provided by the properties.

  • properties:一個定義該原理圖可用選項的物件。

    properties: An object that defines the available options for the schematic.

每個選項都會把 key 與型別、描述和一個可選的別名關聯起來。該型別定義了你所期望的值的形態,並在使用者請求你的原理圖給出用法幫助時顯示這份描述。

Each option associates key with a type, description, and optional alias. The type defines the shape of the value you expect, and the description is displayed when the user requests usage help for your schematic.

關於原理圖的更多自訂選項,請參閱工作區的模式定義。

See the workspace schema for additional customizations for schematic options.

  1. 建立一個 schema.ts 檔案,並定義一個介面,用於存放 schema.json 檔案中定義的各個選項的值。

    Create a schema.ts file and define an interface that stores the values of the options defined in the schema.json file.

export interface Schema { // The name of the service. name: string; // The path to create the service. path?: string; // The name of the project. project?: string; }
projects/my-lib/schematics/my-service/schema.ts (Schematic Interface)
      
      export interface Schema {
  // The name of the service.
  name: string;

  // The path to create the service.
  path?: string;

  // The name of the project.
  project?: string;
}
    
  • name:你要為建立的這個服務指定的名稱。

    name: The name you want to provide for the created service.

  • path:覆蓋為原理圖提供的路徑。預設情況下,路徑是基於當前工作目錄的。

    path: Overrides the path provided to the schematic. The default path value is based on the current working directory.

  • project:提供一個具體專案來執行原理圖。在原理圖中,如果使用者沒有給出該選項,你可以提供一個預設值。

    project: Provides a specific project to run the schematic on. In the schematic, you can provide a default if the option is not provided by the user.

新增範本檔案

Add template files

要把工件新增到專案中,你的原理圖就需要自己的範本檔案。原理圖範本支援特殊的語法來執行程式碼和變數替換。

To add artifacts to a project, your schematic needs its own template files. Schematic templates support special syntax to execute code and variable substitution.

  1. schematics/my-service/ 目錄下建立一個 files/ 資料夾。

    Create a files/ folder inside the schematics/my-service/ folder.

  2. 建立一個名叫 __name@dasherize__.service.ts.template 的檔案,它定義了一個可以用來產生檔案的範本。這裡的範本會產生一個已把 Angular 的 HttpClient 注入到其建構函式中的服務。

    Create a file named __name@dasherize__.service.ts.template that defines a template you can use for generating files. This template will generate a service that already has Angular's HttpClient injected into its constructor.

import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; @Injectable({ providedIn: 'root' }) export class <%= classify(name) %>Service { constructor(private http: HttpClient) { } }
projects/my-lib/schematics/my-service/files/__name@dasherize__.service.ts.template (Schematic Template)
      
      import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class <%= classify(name) %>Service {
  constructor(private http: HttpClient) { }
}
    
  • classifydasherize 方法是實用函式,你的原理圖會用它們來轉換你的範本原始碼和檔名。

    The classify and dasherize methods are utility functions that your schematic will use to transform your source template and filename.

  • name 是工廠函式提供的一個屬性。它與你在模式中定義的 name 是一樣的。

    The name is provided as a property from your factory function. It is the same name you defined in the schema.

添加工廠函式

Add the factory function

現在,你已經有了基礎設施,可以開始定義一個 main 函式來執行要對使用者專案做的各種修改了。

Now that you have the infrastructure in place, you can define the main function that performs the modifications you need in the user's project.

Schematics 框架提供了一個檔案範本系統,它支援路徑和內容範本。系統會操作在這個輸入檔案樹(Tree)中載入的檔案內或路徑中定義的佔位符,用傳給 Rule 的值來填充它們。

The Schematics framework provides a file templating system, which supports both path and content templates. The system operates on placeholders defined inside files or paths that loaded in the input Tree. It fills these in using values passed into the Rule.

關於這些資料結構和語法的詳細資訊,請參閱 Schematics 的 README

For details of these data structures and syntax, see the Schematics README.

  1. 建立主檔案 index.ts 並為你的原理圖工廠函式新增原始碼。

    Create the main file index.ts and add the source code for your schematic factory function.

  2. 首先,匯入你需要的原理圖定義。Schematics 框架提供了許多實用函式來建立規則或在執行原理圖時和使用規則。

    First, import the schematics definitions you will need. The Schematics framework offers many utility functions to create and use rules when running a schematic.

import { Rule, Tree, SchematicsException, apply, url, applyTemplates, move, chain, mergeWith } from '@angular-devkit/schematics'; import { strings, normalize, experimental } from '@angular-devkit/core';
projects/my-lib/schematics/my-service/index.ts (Imports)
      
      import {
  Rule, Tree, SchematicsException,
  apply, url, applyTemplates, move,
  chain, mergeWith
} from '@angular-devkit/schematics';

import { strings, normalize, experimental } from '@angular-devkit/core';
    
  1. 匯入已定義的模式介面,它會為你的原理圖選項提供型別資訊。

    Import the defined schema interface that provides the type information for your schematic's options.

import { Rule, Tree, SchematicsException, apply, url, applyTemplates, move, chain, mergeWith } from '@angular-devkit/schematics'; import { strings, normalize, experimental } from '@angular-devkit/core'; import { Schema as MyServiceSchema } from './schema';
projects/my-lib/schematics/my-service/index.ts (Schema Import)
      
      import {
  Rule, Tree, SchematicsException,
  apply, url, applyTemplates, move,
  chain, mergeWith
} from '@angular-devkit/schematics';

import { strings, normalize, experimental } from '@angular-devkit/core';

import { Schema as MyServiceSchema } from './schema';
    
  1. 要想建構 "產生器原理圖",我們從一個空白的規則工廠開始。

    To build up the generation schematic, start with an empty rule factory.

export function myService(options: MyServiceSchema): Rule { return (tree: Tree) => { return tree; }; }
projects/my-lib/schematics/my-service/index.ts (Initial Rule)
      
      export function myService(options: MyServiceSchema): Rule {
  return (tree: Tree) => {
    return tree;
  };
}
    

這個簡單的規則工廠返回樹而不做任何修改。這些選項都是從 ng generate 命令傳過來的選項值。

This simple rule factory returns the tree without modification. The options are the option values passed through from the ng generate command.

定義一個產生器規則

Define a generation rule

我們現在有了一個框架,可用來建立一些真正修改使用者程式的程式碼,以便對函式庫中定義的服務進行設定。

We now have the framework in place for creating the code that actually modifies the user's application to set it up for the service defined in your library.

使用者安裝過此函式庫的 Angular 工作區中會包含多個專案(應用和函式庫)。使用者可以在命令列中指定一個專案,也可以使用它的預設值。在任何一種情況下,你的程式碼都需要知道應該在哪個專案上應用此原理圖,這樣才能從該專案的配置中檢索資訊。

The Angular workspace where the user has installed your library contains multiple projects (applications and libraries). The user can specify the project on the command line, or allow it to default. In either case, your code needs to identify the specific project to which this schematic is being applied, so that you can retrieve information from the project configuration.

你可以使用傳給工廠函式的 Tree 物件來做到這一點。透過 Tree 的一些方法,你可以訪問此工作空間的完整檔案樹,以便在執行原理圖時讀寫檔案。

You can do this using the Tree object that is passed in to the factory function. The Tree methods give you access to the complete file tree in your workspace, allowing you to read and write files during the execution of the schematic.

獲取專案配置

Get the project configuration

  1. 要確定目標專案,可以使用 Tree.read() 方法在工作空間的根目錄下讀取工作空間配置檔案 angular.json 的內容。將以下程式碼新增到工廠函式中。

    To determine the destination project, use the Tree.read() method to read the contents of the workspace configuration file, angular.json, at the root of the workspace. Add the following code to your factory function.

import { Rule, Tree, SchematicsException, apply, url, applyTemplates, move, chain, mergeWith } from '@angular-devkit/schematics'; import { strings, normalize, experimental } from '@angular-devkit/core'; import { Schema as MyServiceSchema } from './schema'; export function myService(options: MyServiceSchema): Rule { return (tree: Tree) => { const workspaceConfig = tree.read('/angular.json'); if (!workspaceConfig) { throw new SchematicsException('Could not find Angular workspace configuration'); } // convert workspace to string const workspaceContent = workspaceConfig.toString(); // parse workspace string into JSON object const workspace: experimental.workspace.WorkspaceSchema = JSON.parse(workspaceContent); }; }
projects/my-lib/schematics/my-service/index.ts (Schema Import)
      
      import {
  Rule, Tree, SchematicsException,
  apply, url, applyTemplates, move,
  chain, mergeWith
} from '@angular-devkit/schematics';

import { strings, normalize, experimental } from '@angular-devkit/core';

import { Schema as MyServiceSchema } from './schema';

export function myService(options: MyServiceSchema): Rule {
  return (tree: Tree) => {
    const workspaceConfig = tree.read('/angular.json');
    if (!workspaceConfig) {
      throw new SchematicsException('Could not find Angular workspace configuration');
    }

    // convert workspace to string
    const workspaceContent = workspaceConfig.toString();

    // parse workspace string into JSON object
    const workspace: experimental.workspace.WorkspaceSchema = JSON.parse(workspaceContent);
  };
}
    
  • 一定要檢查此上下文是否存在,並丟擲相應的錯誤。

    Be sure to check that the context exists and throw the appropriate error.

  • 把這些內容讀入成字串後,把配置解析成一個 JSON 物件,把它的型別設定為 WorkspaceSchema

    After reading the contents into a string, parse the configuration into a JSON object, typed to the WorkspaceSchema.

  1. WorkspaceSchema 包含工作空間配置的所有屬性,包括一個 defaultProject 值,用來確定如果沒有提供該引數,要使用哪個專案。如果 ng generate 命令中沒有明確指定任何專案,我們就會把它作為後備值。

    The WorkspaceSchema contains all the properties of the workspace configuration, including a defaultProject value for determining which project to use if not provided. We will use that value as a fallback, if no project is explicitly specified in the ng generate command.

if (!options.project) { options.project = workspace.defaultProject; }
projects/my-lib/schematics/my-service/index.ts (Default Project)
      
      if (!options.project) {
  options.project = workspace.defaultProject;
}
    
  1. 現在你有了專案名稱,用它來檢索指定專案的配置資訊。

    Now that you have the project name, use it to retrieve the project-specific configuration information.

const projectName = options.project as string; const project = workspace.projects[projectName]; const projectType = project.projectType === 'application' ? 'app' : 'lib';
projects/my-lib/schematics/my-service/index.ts (Project)
      
      const projectName = options.project as string;

const project = workspace.projects[projectName];

const projectType = project.projectType === 'application' ? 'app' : 'lib';
    

workspace projects 物件包含指定專案的全部配置資訊。

The workspace projects object contains all the project-specific configuration information.

  1. options.path 決定了應用原理圖之後,要把原理圖範本檔案移動到的位置。

    The options.path determines where the schematic template files are moved to once the schematic is applied.

    原理圖模式中的 path 選項預設會替換為當前工作目錄。如果未定義 path,就使用專案配置中的 sourceRootprojectType 來確定。

    The path option in the schematic's schema is substituted by default with the current working directory. If the path is not defined, use the sourceRoot from the project configuration along with the projectType.

if (options.path === undefined) { options.path = `${project.sourceRoot}/${projectType}`; }
projects/my-lib/schematics/my-service/index.ts (Project Info)
      
      if (options.path === undefined) {
  options.path = `${project.sourceRoot}/${projectType}`;
}
    

定義規則

Define the rule

Rule 可以使用外部範本檔案,對它們進行轉換,並使用轉換後的範本返回另一個 Rule 物件。你可以使用範本來產生原理圖所需的任意自訂檔案。

A Rule can use external template files, transform them, and return another Rule object with the transformed template. You can use the templating to generate any custom files required for your schematic.

  1. 將以下程式碼新增到工廠函式中。

    Add the following code to your factory function.

const templateSource = apply(url('./files'), [ applyTemplates({ classify: strings.classify, dasherize: strings.dasherize, name: options.name }), move(normalize(options.path as string)) ]);
projects/my-lib/schematics/my-service/index.ts (Template transform)
      
      const templateSource = apply(url('./files'), [
  applyTemplates({
    classify: strings.classify,
    dasherize: strings.dasherize,
    name: options.name
  }),
  move(normalize(options.path as string))
]);
    
  • apply() 方法會把多個規則應用到原始碼中,並返回轉換後的原始碼。它需要兩個引數,一個原始碼和一個規則陣列。

    The apply() method applies multiple rules to a source and returns the transformed source. It takes 2 arguments, a source and an array of rules.

  • url() 方法會從檔案系統中相對於原理圖的路徑下讀取原始檔。

    The url() method reads source files from your filesystem, relative to the schematic.

  • applyTemplates() 方法會接收一個引數,它的方法和屬性可用在原理圖範本和原理圖檔名上。它返回一條 Rule。你可以在這裡定義 classify()dasherize() 方法,以及 name 屬性。

    The applyTemplates() method receives an argument of methods and properties you want make available to the schematic template and the schematic filenames. It returns a Rule. This is where you define the classify() and dasherize() methods, and the name property.

  • classify() 方法接受一個值,並返回標題格式(title case)的值。比如,如果提供的名字是 my service,它就會返回 MyService

    The classify() method takes a value and returns the value in title case. For example, if the provided name is my service, it is returned as MyService

  • dasherize() 方法接受一個值,並以中線分隔並小寫的形式返回值。比如,如果提供的名字是 MyService,它就會返回 “my-service” 的形式。

    The dasherize() method takes a value and returns the value in dashed and lowercase. For example, if the provided name is MyService, it is returned as my-service.

  • 當應用了此原理圖之後,move 方法會把所提供的原始檔移動到目的地。

    The move method moves the provided source files to their destination when the schematic is applied.

  1. 最後,規則工廠必須返回一條規則。

    Finally, the rule factory must return a rule.

return chain([ mergeWith(templateSource) ]);
projects/my-lib/schematics/my-service/index.ts (Chain Rule)
      
      return chain([
  mergeWith(templateSource)
]);
    

chain() 方法允許你把多個規則組合到一個規則中,這樣就可以在一個原理圖中執行多個操作。這裡你只是把範本規則和原理圖要執行的程式碼合併在一起。

The chain() method allows you to combine multiple rules into a single rule, so that you can perform multiple operations in a single schematic. Here you are only merging the template rules with any code executed by the schematic.

請看原理圖規則函式的一個完整例子。

See a complete exampled of the schematic rule function.

import { Rule, Tree, SchematicsException, apply, url, applyTemplates, move, chain, mergeWith } from '@angular-devkit/schematics'; import { strings, normalize, experimental } from '@angular-devkit/core'; import { Schema as MyServiceSchema } from './schema'; export function myService(options: MyServiceSchema): Rule { return (tree: Tree) => { const workspaceConfig = tree.read('/angular.json'); if (!workspaceConfig) { throw new SchematicsException('Could not find Angular workspace configuration'); } // convert workspace to string const workspaceContent = workspaceConfig.toString(); // parse workspace string into JSON object const workspace: experimental.workspace.WorkspaceSchema = JSON.parse(workspaceContent); if (!options.project) { options.project = workspace.defaultProject; } const projectName = options.project as string; const project = workspace.projects[projectName]; const projectType = project.projectType === 'application' ? 'app' : 'lib'; if (options.path === undefined) { options.path = `${project.sourceRoot}/${projectType}`; } const templateSource = apply(url('./files'), [ applyTemplates({ classify: strings.classify, dasherize: strings.dasherize, name: options.name }), move(normalize(options.path as string)) ]); return chain([ mergeWith(templateSource) ]); }; }
projects/my-lib/schematics/my-service/index.ts
      
      import {
  Rule, Tree, SchematicsException,
  apply, url, applyTemplates, move,
  chain, mergeWith
} from '@angular-devkit/schematics';

import { strings, normalize, experimental } from '@angular-devkit/core';

import { Schema as MyServiceSchema } from './schema';

export function myService(options: MyServiceSchema): Rule {
  return (tree: Tree) => {
    const workspaceConfig = tree.read('/angular.json');
    if (!workspaceConfig) {
      throw new SchematicsException('Could not find Angular workspace configuration');
    }

    // convert workspace to string
    const workspaceContent = workspaceConfig.toString();

    // parse workspace string into JSON object
    const workspace: experimental.workspace.WorkspaceSchema = JSON.parse(workspaceContent);
    if (!options.project) {
      options.project = workspace.defaultProject;
    }

    const projectName = options.project as string;

    const project = workspace.projects[projectName];

    const projectType = project.projectType === 'application' ? 'app' : 'lib';

    if (options.path === undefined) {
      options.path = `${project.sourceRoot}/${projectType}`;
    }

    const templateSource = apply(url('./files'), [
      applyTemplates({
        classify: strings.classify,
        dasherize: strings.dasherize,
        name: options.name
      }),
      move(normalize(options.path as string))
    ]);

    return chain([
      mergeWith(templateSource)
    ]);
  };
}
    

關於規則和實用工具方法的詳細資訊,請參閱預定義規則

For more information about rules and utility methods, see Provided Rules.

執行你的函式庫原理圖

Running your library schematic

在建構函式庫和原理圖之後,你就可以安裝一個原理圖集合來執行你的專案了。下面的步驟介紹瞭如何使用上面建立的原理圖來產生服務。

After you build your library and schematics, you can install the schematics collection to run against your project. The steps below show you how to generate a service using the schematic you created above.

建構你的函式庫和原理圖

Build your library and schematics

在工作區的根目錄下,執行函式庫的 ng build 命令。

From the root of your workspace, run the ng build command for your library.

ng build my-lib
      
      ng build my-lib
    

然後,進入函式庫目錄,建構原理圖

Then, you change into your library directory to build the schematic

cd projects/my-lib npm run build
      
      cd projects/my-lib
npm run build
    

這些函式庫和原理圖都已打包好了,就放在你工作區根目錄下的 dist/my-lib 資料夾中。要執行這個原理圖,你需要把這個函式庫連結到 node_modules 資料夾中。在工作空間的根目錄下,執行 npm link 命令,並把你的可分發函式庫的路徑作為引數。

Your library and schematics are packaged and placed in the dist/my-lib folder at the root of your workspace. For running the schematic, you need to link the library into your node_modules folder. From the root of your workspace, run the npm link command with the path to your distributable library.

npm link dist/my-lib
      
      npm link dist/my-lib
    

執行原理圖

Run the schematic

現在你的函式庫已經安裝完畢,可以使用 ng generate 命令來執行原理圖了。

Now that your library is installed, you can run the schematic using the ng generate command.

ng generate my-lib:my-service --name my-data
      
      ng generate my-lib:my-service --name my-data
    

在控制檯中,你會看到原理圖已經執行過了,my-data.service.ts 檔案被建立在了你的 app 資料夾中。

In the console, you will see that the schematic was run and the my-data.service.ts file was created in your app folder.

CREATE src/app/my-data.service.ts (208 bytes)
      
      CREATE src/app/my-data.service.ts (208 bytes)