Hallo, ich heiĂe Maxim. Seit einigen Jahren entwickle ich Frontends. Ich muss mich oft mit dem Layout verschiedener HTML-Vorlagen befassen. In meiner tĂ€glichen Arbeit verwende ich normalerweise den Webpack Builder mit einer angepassten Mops-Vorlagen-Engine und ich verwende auch die BEM-Methodik. Um mein Leben leichter zu machen, benutze ich ein wunderbares Paket .
Vor kurzem musste ich ein kleines Projekt fĂŒr Angular erstellen, und da ich es gewohnt war, mit meinen Lieblingswerkzeugen zu arbeiten, wollte ich nicht zum nackten HTML zurĂŒckkehren. In diesem Zusammenhang stellte sich das Problem, wie man bempug-Freunde mit einem Winkel findet und nicht nur Freunde findet, sondern auch Komponenten aus cli mit der von mir benötigten Struktur generiert.
Wen interessiert es, wie ich das alles gemacht habe, willkommen bei cat.
Erstellen Sie zunÀchst ein Testprojekt, an dem wir unsere Vorlage testen.
Wir fĂŒhren in der Kommandozeile aus:
ng g test-project
.
In den Einstellungen habe ich den scss-PrÀprozessor gewÀhlt, da ich damit besser arbeiten kann.
Das Projekt wurde erstellt, aber die Standardkomponentenvorlagen in unserem HTML-Code beheben es jetzt. ZunĂ€chst mĂŒssen Sie mit der Mops-Template-Engine eckige Cli-Freunde finden. Dazu habe ich das Paket ng-cli-pug-loader verwendet
Installieren Sie das Paket, gehen Sie dazu in den Projektordner und fĂŒhren Sie Folgendes aus:
ng add ng-cli-pug-loader
.
Jetzt können Sie Mops-Vorlagendateien verwenden. Als NÀchstes schreiben wir den Root-Dekorator der AppComponent-Komponente wie folgt um:
@Component({ selector: 'app-root', templateUrl: './app.component.pug', styleUrls: ['./app.component.scss'] })
Dementsprechend Ă€ndern wir die Dateierweiterung app.component.html in app.component.pug, und der Inhalt wird in der Vorlagensyntax geschrieben. In dieser Datei habe ich alles auĂer dem Router gelöscht.
Zum Schluss erstellen wir unseren Komponentengenerator!
Um Vorlagen zu generieren, mĂŒssen wir unser eigenes Schema erstellen. Ich verwende das schematics-cli-Paket von @ angle-devkit. Installieren Sie das Paket global mit dem folgenden Befehl:
npm install -g @angular-devkit/schematics-cli
.
Ich habe das Schema in einem separaten Verzeichnis auĂerhalb des Projekts mit dem folgenden Befehl erstellt:
schematics blank --name=bempug-component
.
Wir gehen in das erstellte Schema, wir interessieren uns jetzt fĂŒr die Datei src / collection.json. Es sieht so aus:
"$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json", "schematics": { "bempug-component": { "description": "A blank schematic.", "factory": "./bempug-component/index#bempugComponent" } } }
Dies ist eine Beschreibungsdatei unseres Schemas, in der der Parameter "factory" lautet: "./bempug-component/index#bempugComponent": Dies ist die Beschreibung der Hauptfunktion der "factory" unseres Generators.
Anfangs sieht es ungefĂ€hr so ââaus:
import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
Sie können die Funktion standardmĂ€Ăig exportieren, dann kann der Parameter "factory" als "./bempug-component/index" umgeschrieben werden.
Als nÀchstes erstellen Sie im Verzeichnis unseres Schemas die Datei schema.json, in der alle Parameter unseres Schemas beschrieben werden.
{ "$schema": "http://json-schema.org/schema", "id": "SchemanticsForMenu", "title": "Bempug Schema", "type": "object", "properties": { "name": { "type": "string", "$default": { "$source": "argv", "index": 0 } }, "path": { "type": "string", "format": "path", "description": "The path to create the component.", "visible": false }, "project": { "type": "string", "description": "The name of the project.", "$default": { "$source": "projectName" } } } }
Parameter sind in Eigenschaften, nÀmlich:
- Name Name der EntitÀt (in unserem Fall handelt es sich um eine Komponente);
- Pfad ist der Pfad, ĂŒber den der Generator die Komponentendateien erstellt.
- Projekt ist das Projekt selbst, in dem die Komponente generiert wird.
FĂŒgen Sie der Datei einige weitere Parameter hinzu, die in Zukunft benötigt werden.
"module": { "type": "string", "description": "The declaring module.", "alias": "m" }, "componentModule": { "type": "boolean", "default": true, "description": "Patern module per Component", "alias": "mc" }, "export": { "type": "boolean", "default": false, "description": "Export component from module?" }
- Modul wird hier ein Link zu dem Modul gespeichert, in dem die Komponente enthalten sein wird, oder vielmehr das Komponentenmodul;
- componentModule gibt es ein Flag, ob fĂŒr die Komponente ein eigenes Modul erstellt werden soll (dann bin ich zu dem Schluss gekommen, dass es immer erstellt wird und setzt es auf true);
- export: Dies ist das Flag, ob aus dem Modul exportiert werden soll, in das wir unser Komponentenmodul importieren.
Als NĂ€chstes erstellen wir eine Schnittstelle mit den Parametern unserer Komponente, der Datei schema.d.ts.
export interface BemPugOptions { name: string; project?: string; path?: string; module?: string; componentModule?: boolean; module?: string; export?: boolean; bemPugMixinPath?: string; }
Darin duplizieren Eigenschaften Eigenschaften aus schema.json. Als nĂ€chstes bereiten Sie unsere Fabrik vor und gehen zur Datei index.ts. Darin erstellen wir zwei filterTemplates-Funktionen, die fĂŒr das Erstellen eines Moduls fĂŒr eine Komponente abhĂ€ngig vom Wert von componentModule verantwortlich sind, und setupOptions, mit dem die fĂŒr die Factory erforderlichen Parameter eingerichtet werden.
function filterTemplates(options: BemPugOptions): Rule { if (!options.componentModule) { return filter(path => !path.match(/\.module\.ts$/) && !path.match(/-item\.ts$/) && !path.match(/\.bak$/)); } return filter(path => !path.match(/\.bak$/)); } function setupOptions(options: BemPugOptions, host: Tree): void { const workspace = getWorkspace(host); if (!options.project) { options.project = Object.keys(workspace.projects)[0]; } const project = workspace.projects[options.project]; if (options.path === undefined) { const projectDirName = project.projectType === 'application' ? 'app' : 'lib'; options.path = `/${project.root}/src/${projectDirName}`; } const parsedPath = parseName(options.path, options.name); options.name = parsedPath.name; options.path = parsedPath.path; }
Als nÀchstes schreiben wir in die Hauptfunktion:
export function bempugComponent(options: BemPugOptions): Rule { return (host: Tree, context: SchematicContext) => { setupOptions(options, host); const templateSource = apply(url('./files'), [ filterTemplates(options), template({ ...strings, ...options }), move(options.path || '') ]); const rule = chain([ branchAndMerge(chain([ mergeWith(templateSource), ])) ]); return rule(host, context); } }
Die Factory ist bereit und kann bereits Komponentendateien generieren, indem Vorlagen aus dem Dateiordner verarbeitet werden, der noch nicht verfĂŒgbar ist. Es spielt keine Rolle, in meinem Fall erstellen wir einen Ordner mit bempug-Komponentendateien in unserem Schemaordner. Erstellen Sie im Dateiordner den Ordner __name@dasherize__
. WĂ€hrend der Generierung ersetzt die Factory __name@dasherize__
durch den Namen der Komponente.
__name@dasherize__
Dateien
__name@dasherize__
.component.pug Mops Komponentenvorlage__name@dasherize__
.component.spec.ts Unit-Testdatei fĂŒr die Komponente__name@dasherize__
.component.ts Datei der Komponente selbst__name@dasherize__
Komponentenmodul__name@dasherize__
Komponenten-Stylesheet
Jetzt werden wir unserer Factory UnterstĂŒtzung fĂŒr das Aktualisieren von Modulen hinzufĂŒgen. Dazu erstellen wir die Datei add-to-module-context.ts, um die Parameter zu speichern, die die Factory fĂŒr die Arbeit mit dem Modul benötigt.
import * as ts from 'typescript'; export class AddToModuleContext {
FĂŒgen Sie der Fabrik die ModulunterstĂŒtzung hinzu.
const stringUtils = { dasherize, classify };
Wenn Sie nun den Parameter -m <Modulreferenz> zum Befehl cli hinzufĂŒgen, fĂŒgt unser Komponentenmodul dem angegebenen Modul einen Import hinzu und fĂŒgt den Export daraus hinzu, wenn Sie das Flag âexport hinzufĂŒgen. Als nĂ€chstes fĂŒgen wir BEM-UnterstĂŒtzung hinzu. Dazu habe ich die Quellen des npm bempug-Pakets genommen und den Code in einer bempugMixin.pug-Datei erstellt, die ich im allgemeinen Ordner und in einem anderen gemeinsamen Ordner abgelegt habe, damit das Mixin in den gemeinsamen Ordner im Projekt auf dem Winkel kopiert wird.
Unsere Aufgabe ist es, dass dieses Mixin in jeder unserer Vorlagendateien verbunden und beim Generieren neuer Komponenten nicht dupliziert wird. Dazu werden wir diese FunktionalitĂ€t unserer Fabrik hinzufĂŒgen.
import { Rule, SchematicContext, Tree, filter, apply, template, move, chain, branchAndMerge, mergeWith, url, SchematicsException } from '@angular-devkit/schematics'; import {BemPugOptions} from "./schema"; import {getWorkspace} from "@schematics/angular/utility/config"; import {parseName} from "@schematics/angular/utility/parse-name"; import {normalize, strings} from "@angular-devkit/core"; import { AddToModuleContext } from './add-to-module-context'; import * as ts from 'typescript'; import {classify, dasherize} from "@angular-devkit/core/src/utils/strings"; import {buildRelativePath, findModuleFromOptions, ModuleOptions} from "@schematics/angular/utility/find-module"; import {addExportToModule, addImportToModule} from "@schematics/angular/utility/ast-utils"; import {InsertChange} from "@schematics/angular/utility/change"; const stringUtils = { dasherize, classify };
Es ist Zeit, unsere Vorlagendateien auszufĂŒllen.
__name@dasherize__.component.pug
:
include <%= bemPugMixinPath %> +b('<%= name %>') +e('item', {m:'test'}) | <%= name %> works
Was wÀhrend der Generierung in <% =%> angegeben wird, wird durch den Namen der Komponente ersetzt.
__name@dasherize__.component.spec.ts:
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import {NO_ERRORS_SCHEMA} from '@angular/core'; import { <%= classify(name) %>ComponentModule } from './<%= name %>-component.module'; import { <%= classify(name) %>Component } from './<%= name %>.component'; describe('<%= classify(name) %>Component', () => { let component: <%= classify(name) %>Component; let fixture: ComponentFixture<<%= classify(name) %>Component>; beforeEach(async(() => { TestBed.configureTestingModule({ imports: [<%= classify(name) %>ComponentModule], declarations: [], schemas: [ NO_ERRORS_SCHEMA ] }) .compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(<%= classify(name) %>Component); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); });
In diesem Fall wird <% = classify (name)%> verwendet, um den Namen in CamelCase umzuwandeln.
__name@dasherize__.component.ts:
import { Component, OnInit, ViewEncapsulation} from '@angular/core'; @Component({ selector: 'app-<%=dasherize(name)%>-component', templateUrl: '<%=dasherize(name)%>.component.pug', styleUrls: ['./<%=dasherize(name)%>-component.scss'], encapsulation: ViewEncapsulation.None }) export class <%= classify(name) %>Component implements OnInit { constructor() {} ngOnInit(): void { } }
__name@dasherize__-component.module.ts:
import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import {<%= classify(name) %>Component} from './<%= name %>.component'; @NgModule({ declarations: [ <%= classify(name) %>Component, ], imports: [ CommonModule ], exports: [ <%= classify(name) %>Component, ] }) export class <%= classify(name) %>ComponentModule { }
__name@dasherize__-component.scss:
.<%= name %>{ }
Wir erstellen unser Schema mit dem Befehl `` npm run build``.
Alles ist bereit, Komponenten im Projekt zu generieren!
Um dies zu ĂŒberprĂŒfen, kehren Sie zu unserem Angular-Projekt zurĂŒck und erstellen Sie ein Modul.
ng gm test-schema
Als nĂ€chstes fĂŒhren wir `` npm link <absoluter Pfad zum Projektordner mit unserem Schema> '' aus, um unser Schema zu den node_modules des Projekts hinzuzufĂŒgen.
Und wir versuchen die Schaltung mit dem ng g bempug-component:bempug-component test -m /src/app/test-schema/test-schema.module.ts âexport
.
Unser Schema erstellt eine Komponente und fĂŒgt sie beim Export dem angegebenen Modul hinzu.
Wenn das Schema fertig ist, können Sie die Anwendung auf vertrauten Technologien erstellen.
Sie können die endgĂŒltige Version hier sehen , und auch das Paket ist in npm verfĂŒgbar .
Bei der Erstellung des Schemas habe ich Artikel zu diesem Thema verwendet. Ich danke den Autoren.
Vielen Dank fĂŒr Ihre Aufmerksamkeit, alle, die bis zum Ende lesen, Sie sind die Besten!
Und ein weiteres spannendes Projekt erwartet mich. Bis bald!