fluig PowerUp #1 – Distribuindo componentes Angular: do zero à publicação do pacote

Hoje no fluig abordamos um assunto bastante interessante, que é a criação e distribuição de componentes Angular. Criamos uma aplicação do 0 utilizando o @angular/cli, e com a ajuda do ng-packagr empacotamos nosso módulo para em seguida publicá-lo no NPM. Segue abaixo um pequeno passo-a-passo:

___

Instalando o @angular/cli

Primeiramente vamos instalar o @angular/cli globalmente (a versão mais recente, e utilizada neste tutorial foi a 1.7.4):

npm install -g @angular/cli

Gerando o código da aplicação

Com o @angular/cli já instalado, vamos gerar nossa aplicação de sandbox:

ng new meu-modulo-compartilhado

Screenshot from 2018-04-12 14-58-24

Pronto! Com este comando, criamos uma aplicação inteira com todo o processo de build já configurado e todas as dependências necessárias já instaladas. Vamos agora entrar no diretório criado e rodar a aplicação pra ver se está tudo certo:

cd meu-modulo-compartilhado/
npm start

Screenshot from 2018-04-12 15-01-09

Você verá uma mensagem dizendo que a aplicação foi compilada com sucesso, e está disponível na porta 4200. Vamos acessá-la então no navegador pelo endereço http://localhost:4200.

Screenshot from 2018-04-12 15-03-31

Show! Já podemos começar a criar nossos componentes que serão distribuídos!

___

Para este exemplo, eu vou criar um componente que é uma tabela de carros. Ela vai receber um array de objetos, e exibir cada objeto em uma linha da tabela.

Mas você pode criar o componente que você quiser, daí vai da sua criatividade/necessidade.

___

Gerando o código do componente

Vamos aproveitar que já temos o @angular/cli instalado, e utilizá-lo para gerar o módulo que irá comportar nosso componente compartilhado:

ng generate module modules/car-table

Se você reparou bem, eu criei meu módulo dentro de um diretório ‘modules’. Esta pasta será criada pelo @angular/cli, mas em si não é um módulo. É só uma pasta mesmo, que vai comportar nosso(s) módulo(s) compartilhado(s).

Então após executar este comando, nossa estrutura de pastas deve estar assim:

Screenshot from 2018-04-12 15-13-46

Vamos criar agora o componente em si:

ng generate component modules/car-table

E agora nossa estrutura de pastas deve estar assim:

Screenshot from 2018-04-12 15-17-49

Exportando nosso componente

Screenshot from 2018-04-12 15-18-47

Se você reparar na saída do comando anterior, o @angular/cli já atualizou nosso car-table.module.ts, com a declaração do componente criado. Aqui nós aproveitamos, e já exportamos o componente também, por meio do exports, logo abaixo do declarations, para que ele fique acessível no módulo que o importar.

...
import { CarTableComponent } from './car-table.component';

@NgModule({
 ...
 declarations: [CarTableComponent],
 exports: [CarTableComponent]
})
export class CarTableModule { }

Como já estamos em uma aplicação Angular, vamos testar nosso módulo por aqui mesmo antes de distribuí-lo. Basta importar o módulo recém criado em seu app.module.ts:

...
import { CarTableModule } from './modules/car-table/car-table.module';

@NgModule({
 ...
 imports: [
 ...
 CarTableModule
 ]
export class AppModule { }

 

e alterar o app.component.html:

<app-car-table></app-car-table>

e voilá:

Screenshot from 2018-04-13 08-38-53

Great Job Success GIF-source.gif

___

Código do nosso exemplo

Lembra que nosso exemplo é uma tabela de carros, que recebe um array de objetos? Vamos criar o código do nosso componente então:

car-table.component.ts:

...
export class CarTableComponent {
  @Input() cars;
}

car-table.component.html:

<table>
 <thead>
  <tr>
   <th>Marca</th>
   <th>Modelo</th>
   <th>Ano</th>
  </tr>
 </thead>
 <tbody>
  <tr *ngFor="let car of cars">
   <td>{{ car.brand }}</td>
   <td>{{ car.model }}</td>
   <td>{{ car.year }}</td>
  </tr>
 </tbody>
</table>

Screenshot from 2018-04-13 08-59-10

Tá. E aí, e os carros? Bom, a aplicação que for consumir nosso componente é que vai passar a lista de carros que ela quer exibir. Como já estamos em uma aplicação Angular, vamos passar alguns carros por aqui mesmo e testar, antes de distribuir o componente.

Vamos atualizar nosso app.component.html, adicionando a input property cars que recém colocamos no componente da tabela:

<app-car-table [cars]="myCars"></app-car-table>

E vamos declarar a varável myCars no app.component.ts:

...
export class AppComponent {
  myCars = [
    { brand: 'VW', model: 'Fusca', year: 1978 },
    { brand: 'Ford', model: 'Belina', year: 1983 },
    { brand: 'Chevrolet', model: 'Chevette', year: 1985 },
    { brand: 'Fiat', model: 'Spazio', year: 1986 },
  ];
}

Screenshot from 2018-04-13 09-11-36

Pronto! Nosso componente está pronto para ser consumido. Precisamos agora prepará-lo para ser distribuído.

NPM login

Você vai precisar criar uma conta no NPM caso ainda não tenha feito, e realizar o login no seu terminal:

npm login

package.json

A primeira coisa que vamos fazer é dar um nome ao nosso pacote. Para não ter problemas de colisão de nome, vamos dar um escopo à ele. No meu caso, o meu username no NPM é lucasvst, e o nome que eu quero dar pra esse pacote é car-table, então vai ficar @lucasvst/car-table.

Outra coisa a se fazer, é alterar nosso pacote para ser público, então vamos deixar nosso package.json assim:

{
“name”: “@lucasvst/car-table”,

“private”: false,

}

ng-packagr

Vamos utilizar o ng-packagr para empacotar nosso módulo. Se quiser saber mais dá uma olhada aqui, vale a pena. Vamos instalá-lo como uma dependência de desenvolvimento:

npm install --save-dev ng-packagr

Primeiro vamos criar na raiz do projeto o ng-package.json:

{
“$schema”: “./node_modules/ng-packagr/ng-package.schema.json”,
“lib”: {
“entryFile”: “./src/public_api.ts”
},
“whitelistedNonPeerDependencies”: [“.”]
}

Depois, dentro de /src, vamos criar o arquivo public_api.ts. Esse é o cara que vai expor todos os módulos que você quiser compartilhar:

export * from './app/modules/car-table/car-table.module';

E vamos inserir um script a mais em nosso package.json, para invocar o ng-packagr:


“scripts”: {

“packagr”: “./node_modules/.bin/ng-packagr -p ng-package.json”
}

Agora podemos chamar a tarefa de empacotamento pelo NPM:

npm run packagr

Screenshot from 2018-04-13 09-41-55

___

Se sua saída for parecida com a acima, parabéns!

Você acaba de criar um componente que já pode ser importado por outra aplicação Angular!

___

Deverá ter sido criado uma pasta dist/, e dentro dela alguns arquivos:

Screenshot from 2018-04-13 09-48-12

Entre na pasta dist/, e execute o seguinte comando:

npm publish --access public

 

___

Como este pacote tem um escopo, você precisa definir o acesso como public. Caso você tenha uma conta paga no NPM, você poderá também definir o acesso como restricted.

saiba mais aqui

___

E se tudo ocorreu bem, seu pacote já estará disponível para ser baixado pelo NPM:

Screenshot from 2018-04-13 11-08-39

Pausa pro café

Vamos dar uma pausa aqui, e criar uma outra aplicação. Precisamos consumir nossa tabela de carros, em uma aplicação de verdade agora, fora da sandbox que criamos anteriormente.

Vou chamar essa aplicação de classificados-do-teles:

___

lembre-se, essa aplicação deverá ser criada fora da aplicação anteriormente criada. São 2 aplicações diferentes, portanto, se você ainda estiver com seu terminal na aplicação anterior, saia dela para executar o comando abaixo.

___

ng new classificados-do-teles

Com a aplicação gerada, a primeira coisa que vamos fazer é instalar nosso pacote recém publicado:

cd classificados-do-teles
npm install --save @lucasvst/car-table

Para implementar nossa tabela de carros, o app.component.html deverá ter o mesmo conteúdo que o da aplicação anterior:

<app-car-table [cars]="myCars"></app-car-table>

Nosso app.component.ts, também será parecido, com a diferença que agora eu quero exibir outros carros:

...
export class AppComponent {
  myCars = [
    { brand: 'BMW', model: 'X5', year: 2004 },
    { brand: 'Mercedes', model: 'CLASS ML', year: 2006 },
    { brand: 'Mitsubishi', model: 'Lancer', year: 2008 },
    { brand: 'Honda', model: 'Accord', year: 2012 },
  ];
}

Feito isso, precisamos agora importar o módulo CarTableModule, do pacote @lucasvst/car-table em nosso app.module.ts

...
import { CarTableModule } from '@lucasvst/car-table';

@NgModule({
  ...
  imports: [
    ...
    CarTableModule,
  ]
})
export class AppModule { }

E olha quem apareceu de novo!

Screenshot from 2018-04-13 11-26-37

Compilou, passou!

when-your-code-works-first-time

É isso aí pessoal. Se você empacar em algum ponto, com algum erro, verifique o passo-a-passo novamente, verifique se as versões de dependências utilizadas neste artigo são as mesmas que você instalou, e caso tudo pareça bem, mas não funcione, pode me chamar!

Abraços e até a próxima!

Deixe um comentário