Olá, meu nome é Maxim. Há vários anos, estou desenvolvendo front-end. Muitas vezes tenho que lidar com o layout de vários modelos de html. No meu trabalho diário, geralmente uso o construtor webpack com um mecanismo de modelo de pug personalizado e também uso a metodologia BEM. Para facilitar minha vida, uso um pacote maravilhoso .
Recentemente, precisei fazer um pequeno projeto no Angular e, como estava acostumado a trabalhar com minhas ferramentas favoritas, não queria voltar ao html vazio. Nesse contexto, surgiu o problema de como fazer amigos confusos com um angular, e não apenas fazer amigos, mas também gerar componentes do cli com a estrutura de que eu precisava.
Quem se importa como eu fiz tudo, bem-vindo ao gato.
Para começar, crie um projeto de teste no qual testaremos nosso modelo.
Executamos na linha de comando:
ng g test-project
.
Nas configurações, escolhi o pré-processador scss, pois é mais conveniente trabalhar com ele.
O projeto foi criado, mas os modelos de componentes padrão em nosso html agora o corrigem. Primeiro de tudo, você precisa fazer amigos angulares do cli com o mecanismo de modelo de pug, para isso usei o pacote ng-cli-pug-loader
Instale o pacote, para isso, vá para a pasta do projeto e execute:
ng add ng-cli-pug-loader
.
Agora você pode usar arquivos de modelo de pug. Em seguida, reescrevemos o decorador raiz do componente AppComponent para:
@Component({ selector: 'app-root', templateUrl: './app.component.pug', styleUrls: ['./app.component.scss'] })
Assim, alteramos a extensão do arquivo app.component.html para app.component.pug, e o conteúdo é gravado na sintaxe do modelo. Neste arquivo, eu apaguei tudo, exceto o roteador.
Finalmente, vamos começar a criar nosso gerador de componentes!
Para gerar modelos, precisamos criar nosso próprio esquema. Estou usando o pacote schematics-cli do @ angular-devkit. Instale o pacote globalmente com o comando:
npm install -g @angular-devkit/schematics-cli
.
Eu criei o esquema em um diretório separado fora do projeto com o comando:
schematics blank --name=bempug-component
.
Entramos no esquema criado, agora estamos interessados no arquivo src / collection.json. É assim:
"$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json", "schematics": { "bempug-component": { "description": "A blank schematic.", "factory": "./bempug-component/index#bempugComponent" } } }
Este é um arquivo de descrição do nosso esquema, onde o parâmetro é "factory": "./bempug-component/index#bempugComponent": esta é a descrição da função principal da "fábrica" do nosso gerador.
Inicialmente, parece algo como isto:
import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
Você pode exportar a função por padrão, e o parâmetro "factory" pode ser reescrito como "./bempug-component/index".
Em seguida, no diretório do nosso esquema, crie o arquivo schema.json, ele descreverá todos os parâmetros do nosso 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" } } } }
Os parâmetros estão em propriedades, a saber:
- nome nome da entidade (no nosso caso, será um componente);
- Caminho é o caminho pelo qual o gerador criará os arquivos do componente;
- Projeto é o próprio projeto, no qual o componente será gerado;
Adicione mais alguns parâmetros ao arquivo que serão necessários no 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?" }
- O módulo aqui será armazenado em um link para o módulo no qual o componente será incluído, ou melhor, o módulo do componente;
- componentModule existe um sinalizador para criar para o componente seu próprio módulo (então cheguei à conclusão de que ele sempre será criado e configurado como true);
- exportar: este é o sinalizador para exportar do módulo para o qual estamos importando nosso módulo componente;
Em seguida, criamos uma interface com os parâmetros do nosso componente, o arquivo schema.d.ts.
export interface BemPugOptions { name: string; project?: string; path?: string; module?: string; componentModule?: boolean; module?: string; export?: boolean; bemPugMixinPath?: string; }
Nele, as propriedades duplicam propriedades de schema.json. Em seguida, prepare nossa fábrica, vá para o arquivo index.ts. Nele, criamos duas funções filterTemplates, que serão responsáveis por criar um módulo para um componente, dependendo do valor de componentModule, e setupOptions, que define os parâmetros necessários para a 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; }
Em seguida, escrevemos na função 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); } }
A fábrica está pronta e já pode gerar arquivos de componentes processando modelos da pasta de arquivos, que ainda não está disponível. Não importa, criamos uma pasta de arquivos do componente bempug em nossa pasta de esquema no meu caso. Na pasta de arquivos, crie a pasta __name@dasherize__
, durante a geração, a fábrica substituirá __name@dasherize__
pelo nome do componente.
Em seguida, dentro da __name@dasherize__
crie arquivos
__name@dasherize__
modelo de componente de pug__name@dasherize__
arquivo de teste de unidade para o componente__name@dasherize__
do próprio componente__name@dasherize__
componente __name@dasherize__
-component.module.ts__name@dasherize__
-component.scss folha de estilo do componente
Agora adicionaremos suporte para atualizar os módulos em nossa fábrica, para isso criamos o arquivo add-to-module-context.ts para armazenar os parâmetros que a fábrica precisará para trabalhar com o módulo.
import * as ts from 'typescript'; export class AddToModuleContext {
Adicione suporte ao módulo à fábrica.
const stringUtils = { dasherize, classify };
Agora, ao adicionar o parâmetro -m <module reference> ao comando cli, nosso módulo componente adicionará importação ao módulo especificado e adicionará a exportação ao adicionar o sinalizador –export. Em seguida, adicionamos suporte ao BEM. Para fazer isso, peguei as fontes do pacote npm bempug e criei o código em um arquivo bempugMixin.pug, que coloquei na pasta comum e dentro de outra pasta comum para que o mixin seja copiado para a pasta comum no projeto no angular.
Nossa tarefa é que esse mixin esteja conectado em cada um de nossos arquivos de modelo, e não seja duplicado ao gerar novos componentes. Para isso, adicionaremos essa funcionalidade à nossa 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 };
É hora de começar a preencher nossos arquivos de modelo.
__name@dasherize__.component.pug
:
include <%= bemPugMixinPath %> +b('<%= name %>') +e('item', {m:'test'}) | <%= name %> works
O que é especificado em <% =%> durante a geração será substituído pelo nome do 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(); }); });
Nesse caso, <% = classify (name)%> é usado para converter o nome no 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 %>{ }
Fazemos a construção do nosso esquema com o comando `` npm run build``.
Tudo está pronto para gerar componentes no projeto!
Para verificar, volte ao nosso projeto Angular e crie um módulo.
ng gm test-schema
Em seguida, fazemos `` npm link <caminho absoluto para a pasta do projeto com nosso esquema> '', para adicionar nosso esquema aos node_modules do projeto.
E tentamos o circuito com o ng g bempug-component:bempug-component test -m /src/app/test-schema/test-schema.module.ts –export
.
Nosso esquema criará um componente e o adicionará ao módulo especificado com exportação.
O esquema está pronto, você pode começar a fazer o aplicativo em tecnologias familiares.
Você pode ver a versão final aqui , e também o pacote está disponível no npm .
Ao criar o esquema, usei artigos sobre esse tópico, expresso minha gratidão aos autores.
Obrigado por sua atenção, todos que lêem até o fim, você é o melhor!
E outro projeto emocionante me espera. Até breve!