Hola mi nombre es Maxim Durante varios años, he estado haciendo desarrollo front-end. A menudo tengo que lidiar con el diseño de varias plantillas html. En mi trabajo diario, generalmente uso el creador de paquetes web con un motor de plantillas pug personalizado, y también uso la metodología BEM. Para hacerme la vida más fácil, uso un paquete maravilloso .
Recientemente, necesitaba hacer un pequeño proyecto en Angular, y como estaba acostumbrado a trabajar con mis herramientas favoritas, no quería volver al simple HTML. En este sentido, surgió el problema de cómo hacer amigos con un angular, y no solo hacer amigos, sino también generar componentes desde cli con la estructura que necesitaba.
A quién le importa cómo lo hice todo, bienvenido a cat.
Para comenzar, cree un proyecto de prueba en el que probaremos nuestra plantilla.
Ejecutamos en la línea de comando:
ng g test-project
.
En la configuración, elegí el preprocesador scss, ya que es más conveniente para mí trabajar con él.
El proyecto fue creado, pero las plantillas de componentes predeterminadas en nuestro html, ahora lo arreglan. En primer lugar, debes hacer amigos angulares de cli con el motor de plantillas pug, para esto utilicé el paquete ng-cli-pug-loader
Instale el paquete, para esto, vaya a la carpeta del proyecto y ejecute:
ng add ng-cli-pug-loader
.
Ahora puede usar archivos de plantilla pug. A continuación, reescribimos el decorador raíz del componente AppComponent para:
@Component({ selector: 'app-root', templateUrl: './app.component.pug', styleUrls: ['./app.component.scss'] })
En consecuencia, cambiamos la extensión de archivo app.component.html a app.component.pug, y el contenido se escribe en la sintaxis de la plantilla. En este archivo, eliminé todo excepto el enrutador.
Finalmente, ¡comencemos a crear nuestro generador de componentes!
Para generar plantillas, necesitamos crear nuestro propio esquema. Estoy usando el paquete schematics-cli de @ angular-devkit. Instale el paquete globalmente con el comando:
npm install -g @angular-devkit/schematics-cli
.
Creé el esquema en un directorio separado fuera del proyecto con el comando:
schematics blank --name=bempug-component
.
Entramos en el esquema creado, ahora estamos interesados en el archivo src / collection.json. Se ve así:
"$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json", "schematics": { "bempug-component": { "description": "A blank schematic.", "factory": "./bempug-component/index#bempugComponent" } } }
Este es un archivo de descripción de nuestro esquema, donde el parámetro es "factory": "./bempug-component/index#bempugComponent": esta es la descripción de la función principal de la "fábrica" de nuestro generador.
Inicialmente, se parece a esto:
import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
Puede hacer que la función exporte de forma predeterminada, luego el parámetro "fábrica" puede reescribirse como "./bempug-component/index".
A continuación, en el directorio de nuestro esquema, cree el archivo schema.json, describirá todos los parámetros de nuestro esquema.
{ "$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" } } } }
Los parámetros están en propiedades, a saber:
- nombre nombre de la entidad (en nuestro caso será un componente);
- Ruta es la ruta por la cual el generador creará los archivos componentes;
- Proyecto es el proyecto mismo, en el cual se generará el componente;
Agregue algunos parámetros más al archivo que se necesitarán en el futuro.
"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?" }
- El módulo aquí se almacenará un enlace al módulo en el que se incluirá el componente, o más bien el módulo componente;
- componentModule hay un indicador de si se debe crear para el componente su propio módulo (luego llegué a la conclusión de que siempre se creará y lo configurará como verdadero);
- exportar: este es el indicador de si exportar desde el módulo al que estamos importando nuestro módulo componente;
A continuación, creamos una interfaz con los parámetros de nuestro archivo de componentes schema.d.ts.
export interface BemPugOptions { name: string; project?: string; path?: string; module?: string; componentModule?: boolean; module?: string; export?: boolean; bemPugMixinPath?: string; }
En él, las propiedades duplican propiedades de schema.json. Luego, prepare nuestra fábrica, vaya al archivo index.ts. En él, creamos dos funciones de filterTemplates, que serán responsables de crear un módulo para un componente dependiendo del valor de componentModule y setupOptions, que configura los parámetros necesarios para la fábrica.
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; }
A continuación, escribimos en la función principal:
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); } }
La fábrica está lista y ya puede generar archivos de componentes procesando plantillas desde la carpeta de archivos, que aún no está disponible. No importa, en mi caso creamos una carpeta de archivos de componentes de Bempug. En la carpeta de archivos, cree la carpeta __name@dasherize__
, durante la generación, la fábrica reemplazará __name@dasherize__
con el nombre del componente.
Luego, dentro de la __name@dasherize__
cree archivos
__name@dasherize__
.component.pug plantilla de componente pug__name@dasherize__
.component.spec.ts unidad de prueba para el componente__name@dasherize__
.component.ts archivo del componente en sí__name@dasherize__
-component.module.ts módulo de componente__name@dasherize__
-component.scss hoja de estilo de componentes
Ahora agregaremos soporte para actualizar módulos a nuestra fábrica, para esto crearemos el archivo add-to-module-context.ts para almacenar los parámetros que la fábrica necesitará para trabajar con el módulo.
import * as ts from 'typescript'; export class AddToModuleContext {
Agregue soporte de módulos a la fábrica.
const stringUtils = { dasherize, classify };
Ahora, al agregar el parámetro -m <referencia de módulo> al comando cli, nuestro módulo componente agregará importación al módulo especificado y agregará la exportación desde este al agregar el indicador –exportar. A continuación, agregamos soporte BEM. Para hacer esto, tomé las fuentes del paquete npm bempug e hice el código en un archivo bempugMixin.pug, que coloqué en la carpeta común y dentro de otra carpeta común para que el mixin se copie en la carpeta común en el proyecto en el angular.
Nuestra tarea es que este mixin esté conectado en cada uno de nuestros archivos de plantilla, y no esté duplicado al generar nuevos componentes, para esto agregaremos esta funcionalidad a nuestra fábrica.
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 hora de comenzar a completar nuestros archivos de plantilla.
__name@dasherize__.component.pug
:
include <%= bemPugMixinPath %> +b('<%= name %>') +e('item', {m:'test'}) | <%= name %> works
Lo que se especifica en <% =%> durante la generación será reemplazado por el nombre del componente.
__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(); }); });
En este caso, <% = classify (name)%> se usa para enviar el nombre a CamelCase.
__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 %>{ }
Realizamos la compilación de nuestro esquema con el comando `` npm run build``.
¡Todo está listo para generar componentes en el proyecto!
Para verificar, regrese a nuestro proyecto Angular y cree un módulo.
ng gm test-schema
A continuación, hacemos `` npm link <ruta absoluta a la carpeta del proyecto con nuestro esquema> '', para agregar nuestro esquema a los node_modules del proyecto.
E intentamos el circuito con el ng g bempug-component:bempug-component test -m /src/app/test-schema/test-schema.module.ts –export
.
Nuestro esquema creará un componente y lo agregará al módulo especificado con la exportación.
El esquema está listo, puede comenzar a hacer la aplicación en tecnologías familiares.
Puede ver la versión final aquí , y también el paquete está disponible en npm .
Al crear el esquema utilicé artículos sobre este tema, expreso mi gratitud a los autores.
Gracias por su atención, todos los que leen hasta el final, ¡son los mejores!
Y otro proyecto emocionante me espera. Hasta pronto!