Home > Web Front-end > JS Tutorial > body text

What are Angular Schematics? How to build? (detailed explanation)

青灯夜游
Release: 2022-02-22 11:20:50
forward
2784 people have browsed it

What are Angular Schematics? How to develop your Angular Schematics locally? The following article will give you a detailed introduction and use an example to get better familiar with it. I hope it will be helpful to you!

What are Angular Schematics? How to build? (detailed explanation)

What are Angular Schematics?

Angular Schematics is a template-based, Angular-specific code generator. Of course, it not only generates code, it can also modify our code. It allows us to implement our code based on Angular CLI. Some automated operations of my own. [Related tutorial recommendations: "angular tutorial"]

I believe that everyone has used ng generate component component-name, ## while developing Angular projects. #ng add @angular/materials, ng generate module module-name, these are some CLIs that have been implemented for us in Angular, so how should we implement them in our own projects based on our own What about the project's CLI? This article will be introduced based on our practice in ng-devui-admin. We welcome your continued attention. We will launch a richer CLI in the future to help you build an Admin page faster.

How to develop your Angular Schematics locally

To develop locally you need to install

schematics Scaffolding

npm install -g @angular-devkit/schematics-cli

# 安装完成之后新建一个schematics项目
schematics blank --name=your-schematics
Copy after login

After creating a new project you will see the following The directory structure means that you have successfully created a

shematics project.

What are Angular Schematics? How to build? (detailed explanation)

Important file introduction

  • tsconfig.json: Mainly related to project packaging and compilation, not here Give a detailed introduction

  • collection.json: related to your CLI command, used to define your related commands

  • {
      "$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json",
      "schematics": {
        "first-schematics": {
          "description": "A blank schematic.",
          "factory": "./first-schematics/index#firstSchematics"
        }
      }
    }
    Copy after login

first-schematics: The name of the command, which can be run in the project via ng g first-schematics:first-schematics. description: Description of this command. factory: Entry function for command execution There is usually another attribute schema, which we will explain later.

  • index.ts: Implement the relevant logic of your command in this file
  • import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
    
    export function firstSchematics(_options: any): Rule {
      return (tree: Tree, _context: SchematicContext) => {
        return tree;
      };
    }
    Copy after login
Here we first look at a few parameters that need to be understood :

tree: Here you can understand tree as our entire angular project. You can add files, modify files, and delete files through tree. _context: This parameter is the context in which schematics is run. For example, you can execute npm install through context. Rule: The operation logic formulated for us.

Implementing an ng-add directive

Now we will get better familiar with it by implementing an

ng-add directive.

It is also based on the projects we have created above.

New command-related files<span style="font-size: 18px;"></span>

First we create a new directory under the

src directoryng-add , and then add three files index.ts, schema.json, schema.ts in the directory. After that, your directory structure should be as follows :

What are Angular Schematics? How to build? (detailed explanation)

##Configurationcollection.json<span style="font-size: 18px;"></span> Then we configure this command in

collection.json

: <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">{ &quot;$schema&quot;: &quot;../node_modules/@angular-devkit/schematics/collection-schema.json&quot;, &quot;schematics&quot;: { ..., &quot;ng-add&quot;: { &quot;factory&quot;: &quot;./ng-add/index&quot;, &quot;description&quot;: &quot;Some description about your schematics&quot;, &quot;schema&quot;: &quot;./ng-add/schema.json&quot; } } }</pre><div class="contentsignin">Copy after login</div></div>

in <span style="font-size: 18px;"></span>files<span style="font-size: 18px;"></span>Add the file we want to insert into the directory<span style="font-size: 18px;"></span>For the syntax of

template

, please refer to ejs syntax

app.component.html.template

<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:html;toolbar:false;">&lt;div class=&quot;my-app&quot;&gt; &lt;% if (defaultLanguage === &amp;#39;zh-cn&amp;#39;) { %&gt;你好,Angular Schematics!&lt;% } else { %&gt;Hello, My First Angular Schematics!&lt;% } %&gt; &lt;h1&gt;{{ title }}&lt;/h1&gt; &lt;/div&gt;</pre><div class="contentsignin">Copy after login</div></div>

app.component.scss.template

<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">.app { display: flex; justify-content: center; align-item: center; }</pre><div class="contentsignin">Copy after login</div></div>

app.component.ts.template

<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">import { Component } from &amp;#39;@angular/core&amp;#39;; @Component({ selector: &amp;#39;app-root&amp;#39;, templateUrl: &amp;#39;./app.component.html&amp;#39;, styleUrls: [&amp;#39;./app.component.scss&amp;#39;] }) export class AppComponent { title = &lt;% if (defaultLanguage === &amp;#39;zh-cn&amp;#39;) { %&gt;&amp;#39;你好&amp;#39;&lt;% } else { %&gt;&amp;#39;Hello&amp;#39;&lt;% } %&gt;; }</pre><div class="contentsignin">Copy after login</div></div>

Start implementing the command logic<span style="font-size: 18px;"></span>

    schema.json
  • : Define the username in this file Interaction
    {
      "$schema": "http://json-schema.org/schema",
      "id": "SchematicsDevUI",
      "title": "DevUI Options Schema",
      "type": "object",
      "properties": {
        "defaultLanguage": {
          "type": "string",
          "description": "Choose the default language",
          "default": "zh-cn",
          "x-prompt": {
            "message": "Please choose the default language you want to use: ",
            "type": "list",
            "items": [
              {
                "value": "zh-cn",
                "label": "简体中文 (zh-ch)"
              },
              {
                "value": "en-us",
                "label": "English (en-us)"
              }
            ]
          }
        },
        "i18n": {
          "type": "boolean",
          "default": true,
          "description": "Config i18n for the project",
          "x-prompt": "Would you like to add i18n? (default: Y)"
        }
      },
      "required": []
    }
    Copy after login
  • In the above definition, our command will receive two parameters,
defaultLanguage

, i18n, we use defaultLanguage Take an example to explain the related configuration of parameters: <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">{ &quot;defaultLanguage&quot;: { &quot;type&quot;: &quot;string&quot;, &quot;description&quot;: &quot;Choose the default language&quot;, &quot;default&quot;: &quot;zh-cn&quot;, &quot;x-prompt&quot;: { &quot;message&quot;: &quot;Please choose the default language you want to use: &quot;, &quot;type&quot;: &quot;list&quot;, &quot;items&quot;: [ { &quot;value&quot;: &quot;zh-cn&quot;, &quot;label&quot;: &quot;简体中文 (zh-ch)&quot; }, { &quot;value&quot;: &quot;en-us&quot;, &quot;label&quot;: &quot;English (en-us)&quot; } ] } } }</pre><div class="contentsignin">Copy after login</div></div><p><code>type 代表该参数的类型是 stringdefault 为该参数的默认值为 zh-cnx-prompt 定义与用户的交互,message 为我们对用户进行的相关提问,在这里我们的 typelist 代表我们会为用户提供 items 中列出的选项供用户进行选择。

  • schema.ts:在该文件中定义我们接收到的参数类型
export interface Schema {
  defaultLanguage: string;
  i18n: boolean;
}
Copy after login
  • index.ts:在该文件中实现我们的操作逻辑,假设在此次 ng-add 操作中,我们根据用户输入的 defaultLanguage, i18n 来对用户的项目进行相应的更改,并且插入相关的 npm 包,再进行安装。
import {
  apply,
  applyTemplates,
  chain,
  mergeWith,
  move,
  Rule,
  SchematicContext,
  SchematicsException,
  Tree,
  url
} from &#39;@angular-devkit/schematics&#39;;
import { NodePackageInstallTask } from &#39;@angular-devkit/schematics/tasks&#39;;
import { Schema as AddOptions } from &#39;./schema&#39;;

let projectWorkspace: {
  root: string;
  sourceRoot: string;
  defaultProject: string;
};

export type packgeType = &#39;dependencies&#39; | &#39;devDependencies&#39; | &#39;scripts&#39;;
export const PACKAGES_I18N = [
  &#39;@devui-design/icons@^1.2.0&#39;,
  &#39;@ngx-translate/core@^13.0.0&#39;,
  &#39;@ngx-translate/http-loader@^6.0.0&#39;,
  &#39;ng-devui@^11.1.0&#39;
];
export const PACKAGES = [&#39;@devui-design/icons@^1.2.0&#39;, &#39;ng-devui@^11.1.0&#39;];
export const PACKAGE_JSON_PATH = &#39;package.json&#39;;
export const ANGULAR_JSON_PATH = &#39;angular.json&#39;;

export default function (options: AddOptions): Rule {
  return (tree: Tree, context: SchematicContext) => {
    // 获取项目空间中我们需要的相关变量
    getWorkSpace(tree);

    // 根据是否选择i18n插入不同的packages
    const packages = options.i18n ? PACKAGES_I18N : PACKAGES;
    addPackage(tree, packages, &#39;dependencies&#39;);

    // 执行 npm install
    context.addTask(new NodePackageInstallTask());

    // 自定义的一系列 Rules
    return chain([removeOriginalFiles(), addSourceFiles(options)]);
  };
}
Copy after login

下面时使用到的函数的具体实现:

// getWorkSpace
function getWorkSpace(tree: Tree) {
  let angularJSON;
  let buffer = tree.read(ANGULAR_JSON_PATH);
  if (buffer) {
    angularJSON = JSON.parse(buffer.toString());
  } else {
    throw new SchematicsException(
      &#39;Please make sure the project is an Angular project.&#39;
    );
  }

  let defaultProject = angularJSON.defaultProject;
  projectWorkspace = {
    root: &#39;/&#39;,
    sourceRoot: angularJSON.projects[defaultProject].sourceRoot,
    defaultProject
  };

  return projectWorkspace;
}
Copy after login
// removeOriginalFiles
// 根据自己的需要选择需要删除的文件
function removeOriginalFiles() {
  return (tree: Tree) => {
    [
      `${projectWorkspace.sourceRoot}/app/app.component.ts`,
      `${projectWorkspace.sourceRoot}/app/app.component.html`,
      `${projectWorkspace.sourceRoot}/app/app.component.scss`,
      `${projectWorkspace.sourceRoot}/app/app.component.css`
    ]
      .filter((f) => tree.exists(f))
      .forEach((f) => tree.delete(f));
  };
}
Copy after login

将 files 下的文件拷贝到指定的路径下,关于 chain, mergeWith, apply, template 的详细使用方法可以参考 Schematics

// addSourceFiles
function addSourceFiles(options: AddOptions): Rule {
  return chain([
    mergeWith(
      apply(url(&#39;./files&#39;), [
        applyTemplates({
          defaultLanguage: options.defaultLanguage
        }),
        move(`${projectWorkspace.sourceRoot}/app`)
      ])
    )
  ]);
}
Copy after login
// readJson
function readJson(tree: Tree, file: string, type?: string): any {
  if (!tree.exists(file)) {
    return null;
  }

  const sourceFile = tree.read(file)!.toString(&#39;utf-8&#39;);
  try {
    const json = JSON.parse(sourceFile);
    if (type && !json[type]) {
      json[type] = {};
    }
    return json;
  } catch (error) {
    console.log(`Failed when parsing file ${file}.`);
    throw error;
  }
}

// writeJson
export function writeJson(tree: Tree, file: string, source: any): void {
  tree.overwrite(file, JSON.stringify(source, null, 2));
}

// readPackageJson
function readPackageJson(tree: Tree, type?: string): any {
  return readJson(tree, PACKAGE_JSON_PATH, type);
}

// writePackageJson
function writePackageJson(tree: Tree, json: any): any {
  return writeJson(tree, PACKAGE_JSON_PATH, json);
}

// addPackage
function addPackage(
  tree: Tree,
  packages: string | string[],
  type: packgeType = &#39;dependencies&#39;
): Tree {
  const packageJson = readPackageJson(tree, type);

  if (packageJson == null) {
    return tree;
  }

  if (!Array.isArray(packages)) {
    packages = [packages];
  }
  packages.forEach((pck) => {
    const splitPosition = pck.lastIndexOf(&#39;@&#39;);
    packageJson[type][pck.substr(0, splitPosition)] = pck.substr(
      splitPosition + 1
    );
  });

  writePackageJson(tree, packageJson);
  return tree;
}
Copy after login

为了保持 index.ts 文件的简洁,可以将相关操作的方法抽取到一个新的文件中进行引用。

测试 <span style="font-size: 18px;">ng-add</span>

至此我们已经完成了 ng-add 命令,现在我们对该命令进行测试:

  • ng new test 初始化一个 Angular 项目
  • cd test && mkdir libs 在项目中添加一个 libs 文件夹,将图中标蓝的文件拷贝到其中

What are Angular Schematics? How to build? (detailed explanation)

  • 之后在命令行中执行 npm link libs/
  • link 完成之后 cd libs && npm run build && cd ..
  • 现在执行 ng add first-schematics 之后会看到如下提示

What are Angular Schematics? How to build? (detailed explanation)

  • 最后我们通过 npm start 来查看执行的结果如下

What are Angular Schematics? How to build? (detailed explanation)

结语

综上简单介绍了一个 Schematics 的实现,更多的一些应用欢迎大家查看 ng-devui-admin 中的实现。

更多编程相关知识,请访问:编程学习!!

The above is the detailed content of What are Angular Schematics? How to build? (detailed explanation). For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:juejin.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template