Loading repository data…
Loading repository data…
ortegavan / repository
Este repositório contém os exercícios/aprendizados da Mentoria Angular Pro de Paolo Almeida e Andrew Rosário
Este documento contém os exercícios feitos em aula + minhas notas pessoais sobre a Mentoria Angular Pro de Paolo Almeida e Andrew Rosário.
Em aula, os mentores utilizam scss. Eu optei por usar css por ter mais prática com ele. Os scripts do Nx que criam libs com scss, na mentoria, foram alterados aqui para css.
Se este documento for útil para você, considere deixar uma :star: no repositório.
Explicação do escopo do projeto: trata-se de um e-commerce com as funcionalidades de cadastro/login, uma home e um catálogo de produtos. O projeto contempla o front-end desenvolvido em Angular com Nx e o uso de uma API fake disponível no https://mockapi.io.
Criação do projeto utilizando o comando:
npx create-nx-workspace@latest ecommerce --preset=angular-standalone
Execução do projeto com o comando:
nx serve
Criação da biblioteca de layout com o comando:
nx g @nx/angular:library --name=layout --directory=modules/feature/layout --projectNameAndRootFormat=as-provided --standalone=false --style=css
Visualização do gráfico de dependências do projeto com o comando:
nx graph
Criado componente header usando Nx Console a partir da pasta modules/feature/layout/src/lib passando as opções export=true e standalone=false. O comando gerado a partir do Nx Console foi:
npx nx generate @nx/angular:component --name=header --directory=header --export=true --standalone=false --nameAndDirectoryFormat=as-provided --no-interactive
Consumo da biblioteca de layout no projeto principal:
LayoutModule em app.component.ts;ecommerce-header em app.component.html;Feito isso, nx lint e nx test foram executados para garantir que o projeto está funcionando corretamente.
Primeiro, foi estilizado o componente header. Em seguida, foram escritos testes unitários para o componente header:
it(`should contain title`, () => {
const header: HTMLHeadElement =
fixture.nativeElement.querySelector('header');
expect(header.textContent).toBe('Ecommerce');
});
E para o componente app:
it(`should contain header`, () => {
const header: HTMLElement = fixture.nativeElement.querySelector('header');
expect(header).toBeTruthy();
});
Instalado husky + lint-staged para rodar nx lint e nx test antes de cada commit. Seguem comandos:
npx husky-init && npm install
npm install lint-staged
Para configurar:
npm test por npx lint-staged no arquivo .husky/pre-commit;.lintstagedrc na raiz da aplicação com o seguinte conteúdo:{
"{src,modules}/**/*.{js,ts,jsx,tsx,json,html,css,scss}": [
"nx affected:lint --fix --uncommitted",
"nx affected:test",
"nx format:write --uncommited"
]
}
rules do arquivo eslintrc.base.json para permitir o uso de console.warn e console.error no código mas não permitir console.log:"no-console": [
"error", {
"allow": ["warn", "error"]
}
],
Criado o módulo product-data-access com o comando:
npx nx g @nx/angular:library --name=product-data-access --directory=modules/data-access/product --projectNameAndRootFormat=as-provided
O componente product-data-access foi excluído e removido da index.ts.
Notas
Para que o commit funcionasse, precisei alterar na configuração do lint-staged:
js e css do nx lint;--passWithNoTests no nx test.Criada a model Product em modules/data-access/product/src/lib/models/product.ts:
export type Product = {
createdAt: string;
name: string;
price: string;
description: string;
image: string;
id: string;
quantity: number;
};
Criado o serviço para busca de produtos com o comando:
npx nx g @schematics/angular:service --name=product-search --project=product-data-access --flat=false
Em src/app/app.config.ts foi importado o HttpClient via provideHttpClient().
Por último, foi implementado o teste através do HttpClientTestingModule e HttpTestingController para o serviço ProductSearchService.
Alterei meu .prettierrc para usar 4 espaços em vez de 2 nas formatações:
{
"singleQuote": true,
"useTabs": false,
"tabWidth": 4
}
Rodei o comando abaixo para reformatar todos os arquivos do projeto com o novo espaçamento:
nx format:write --all
Reestilizei alguns componentes e apliquei uma nova fonte ao projeto:
index.html;modules/feature/layout/src/lib/header/header.component.css;styles.css.🌸 Floreei 🌸 este README.md.
Foi instalado/adicionado ao projeto o Angular Material com os comandos abaixo:
npm install @angular/material
npx nx g @angular/material:ng-add --project=ecommerce
Em seguida, foi criado o módulo Product Search com o comando:
npx nx g @nx/angular:library --name=product-search --directory=modules/feature/product/search --projectNameAndRootFormat=as-provided --style=css
Os dados do Data Access foram exportados via modules/data-access/product/src/lib/index.ts:
export * from './lib/mocks/product.mock';
export * from './lib/product-search/product-search.service';
O componente product-search foi implementado usando o componente Autocomplete do Angular Material;
O padrão de composição foi aplicado no componente header:
<header class="header">
<h1 class="logo">{{ title }}</h1>
<ng-content></ng-content>
<ng-content select="[right]"></ng-content>
</header>
E o componente foi então consumido no app.component.html:
<ecommerce-header title="e-Commerce">
<ecommerce-product-search></ecommerce-product-search>
<p right>Login</p>
</ecommerce-header>
<router-outlet></router-outlet>
Por fim, para os testes rodarem corretamente, foram desabilitadas as animações do Angular Material no product.search.component.spec.ts:
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
subscriptSizing do campo de busca para dynamic para alinhar o componente verticalmente;index.html porque já havia configurado a Montserrat;app.component.html até definirmos o próximo componente.Foi implementada a busca de produtos no componente product-search com o uso do FormControl e operadores do RxJS para evitar requisições desnecessárias e foi utilizado pipe async para subscrever o observable no template.
this.products$ = this.control.valueChanges.pipe(
debounceTime(333),
distinctUntilChanged(),
filter((text) => text.length > 1),
switchMap((text) => this.productSearchService.searchByName(text))
);
Foram implementados testes para o componente product-search e para o serviço ProductSearchService. Utilizamos FakeAsync + tick para simular o tempo de espera da requisição e usamos spy para verificar se o método searchByName foi chamado.
Nesta aula, criamos o módulo home com o comando:
nx g @nx/angular:library --name=home --directory=modules/feature/home --lazy=true --routing=true --projectNameAndRootFormat=as-provided --style=css
Discutimos as estratégias de preloading disponíveis no Angular e implementamos o lazy loading do módulo home. Aprendi que o lazy loading pode ser configurado usando loadChildren apontando para o módulo de rotas:
export const appRoutes: Route[] = [
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{
path: 'home',
loadChildren: () => import('@ecommerce/home').then((r) => r.homeRoutes),
},
];
Vimos o lazy loading em ação ao inspecionar a aplicação no navegador:
Mais sobre as estratégias de preloading pode ser visto neste post.
Implementamos a seção de produtos recomendados, por enquanto com mock de dados. Conversamos sobre HTML semântico e a importância de usar tags apropriadas para melhorar a acessibilidade e SEO. Famos sobre o padrão BEM para nomenclatura de classes do CSS.
Mais sobre padrões de acessibilidade pode ser visto neste link.
Criamos o serviço de produtos recomendados agora buscando da API (antes usávamos mock) e refatoramos a home para separar o código responsável pelo card de produto.
Criamos a lib para exibir os detalhes de um produto com o comando:
npx nx g @nx/angular:library --name=product-detail --directory=modules/feature/product/detail --lazy=true --routing=true --projectNameAndRootFormat=as-provided --style=css
Habilitamos a captura de parâmetros através de input no componente adicionando a função withComponentInputBinding no app.config.ts:
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(appRoutes, withComponentInputBinding()),
provideHttpClient(),
provideAnimationsAsync(),
],
};
Para os testes passarem, foi necessário adicionar o RouterTestingModule em product-detail.component.spec.ts.
Implementamos module boundaries para garantir as regras:
type:data-access deve ser capaz de importar de type:data-access;type:feature deve ser capaz de importar de type:feature, type:ui e type:data-access;type:ui deve ser capaz de importar de type:ui e type:data-accessPara aplicar as regras, precisamos dos dois passos:
project.json (exemplo abaixo para a lib product-data-access):"tags": ["type:data-access"]
.eslintrc.base.json no array depConstraints do plugin @nx/enforce-module-boundaries:{
"sourceTag": "type:data-access",
"onlyDependOnLibsWithTags": ["type:data-access"]
},
{
"sourceTag": "type:feature",
"onlyDependOnLibsWithTags": [
"type:feature",
"type:ui",
"type:data-access"
]
},
{
"sourceTag": "type:ui",
"onlyDependOnLibsWithTags": [
"type:ui",
"type:data-access"
]
}
Mais sobre module boundaries pode ser visto neste link e neste link.
Criamos um pipe customizado e vimos a diferença entre um pipe puro e um pipe impuro. O pipe customizado foi criado com o comando:
nx g @nx/angular:pipe --name=quantity-description --directory=modules/feature/product/detail/src/lib/pipes/quantity-description --nameAndDirectoryFormat=as-provided
Vimos a importância de utilizar o pipe em vez de chamar funções diretamente no temp