Você está na página 1de 10

Boas Práticas FE

Indice
1. Código Bonito
2. Página com Subscrições
3. Componentes Dinâmicos, Componentes Específicos e Interfaces
4. Funções de TS
Código Bonito
Antes de se dar uma issue como terminada e enquanto se vai desenvolvendo, deve-se ter em atenção o seguinte:

"TODOS" devem ser adicionados com a tag da issue;


Variáveis Tipificadas;
Métodos, Variáveis, Componentes e Interfaces com nomes óbvios, ao ler os seus nomes deve-se logo perceber o
melhor possível ao que se referem e o que fazem;
Novos ficheiros com nomes óbvios, quando se criam ficheiros sem ser com o ng generate deve-se também deixar claro
o que é, com o padrão feature.type.ts, por exemplo:
client.interface.ts; - sei logo que é uma interface
Simplificar e separar ter em atenção a estrutura das pastas, se o código está simplicado usando componentes separados
(dinamicos ou não) e funções partilhadas ou separadas quando possivel
Atenção às subscrições cuidado para não deixar o flow preso por causa das subscrições
Sonar Lint/Es Lint instalado em local (ler Sonar-EsLint-FE-README.md)
Sem código replicado *
Não pode ter deprecated *
Não pode ter CS (code smells), exemplos: *
Improper Names
Dead Code (código comentado ou inútil)
Long Parameter List (de funções e assim)
Long Functions
Compilado e testado em local

ng build
npm test (e testes humanos)

Testado em dev (testes humanos)

*Podem não ser seguidos totalmente à risca, por vezes tem de existir código duplicado ou tem de se usar código deprecated, mas deve ser

analisado.

Usando o SonarLint/ESLint, são mencionados os problemas existentes no código, pelo que esses devem sempre ser analisados e
tentar ao máximo ter o menor número de problemas possível.

Além disso ao correr npm test também são mostrados alguns warnings e erros que também são relacionados pelo ESLint, pelo
que quando possível tentar também corrigir os warnings que vão surgindo ao correr o npm test.
Estrutura Projetos
Os nossos projetos de Angular devem estar organizados por módulos consoante as secções do projeto.
O shared também deve estar organizado.
Nem todos os projetos têm esta estrutura mas tentar fazer algo assim:

app
├── core
│ ├── constants
│ ├── auth
│ ├── interceptor
│ └── ...
├── development
│ ├── ...
│ ├── module1
│ │ ├── pages
| | │ ├── page1 // coisas específicas desta page?
| | │ │ ├── services
| | | │ ├── components
| | | │ └── common
| │ | └── ...
│ │ ├── services // coisas partilhados entre várias pages deste module
│ │ ├── interfaces
│ │ ├── common
│ │ ├── module1.module.ts
│ │ └── module1-routing.module.ts
│ ├── module2
│ ├── ...
│ └── development.route.ts
├── shared
│ ├── components
│ ├── services
│ ├── interfaces
│ ├── common
│ └── ...
└── ...
Página com Subscrições
Na maior parte dos nossos projetos temos páginas de TS que o que fazem é conectarem-se com serviços de FE que fazem
chamadas a serviços de Swagger para fazer pedidos e receber respostas do BE.
Pelo que para isso temos uma estrutura também estabelecida.

Componente TS
Variáveis

isLoading = true -> Será a variável que controla a renderização do conteúdo da página de HTML, pelo que só deve
passar a false, quando já se têm todos os dados e já se fizeram todos os métodos necessários
Para cada set de dados necessários existem váriáveis tipificadas onde esses dados serão guardados.
Para controlar se esses dados já chegaram, isto deve ser feito ou através de um boleano especifico para esses dados ou
se a variável for iniciada a undefined, verfica-se a sua existência. exemplo:

public dataToShow?: DataToShow[];


public otherDataToShow: OtherataToShow[] = [];
public isLoadingOtherDataToShow = true;

Métodos

ngOnInit() -> Este é o primeiro método que é feito quando se entra na página, pelo que deve chamar qualquer tipo de
métodos que devam logo ser feitos:

initRequests() -> Deve chamar o Init Requests para que as subscrições aos dados sejam iniciadas e ficar à
escuta;
getData() -> Para que sejam chamados os dados necessários
funções que não dependam dos dados -> isto é, podem ser chamadas funções que o que fazem é apenas
inicializar algo, funções que devam ser feitas uma vez e que não dependam de nenhuns dados, exemplo:
initTable, esta função por norma serve para definir uma tabela, ou seja, define as suas colunas e botões
que terá, não precisa de já ter dados para isso.

initRequests() -> Este método contém as várias subscrições aos Subjects de um serviço. As subscrições são inicializadas
e ficam apenas à escuta, o código dentro de cada subscrição não é logo corrido. Só entram lá quando o serviço emite
valor. Ou seja, quando os dados chegam, as suas variáveis especificas são populadas por esses dados e se tiverem
associadas a algum booleano esse passa a false.
Aqui devem estar as subscrições de gets, creates, updates, deletes.

getData() -> Este método deve chamar os pedidos de get do Serviço, para buscar todos os dados necessários.

dataArrived() -> Este método deve ser chamado em todas as subscrições que fizer sentido e vai alterar o isLoading da
página para false, mas deve estar preso e validar a existência de todos os dados de que depende.
Se passar nas condições devem ser feitas todas as funções que dependam dos dados, como transformas dados de uma
tabela, iniciar um formulário de um detalhe de algum objeto e só no fim é que se trocao o isLoading = false.

ngOnDestroy() -> fecha as subscrições todas da página;


Nalguns projetos já existe umas interfaces:

SubscriptionGetDataComponent
SubscriptionComponent

Estas interfaces ao serem implementadas obrigam a implementação destes métodos.

Exemplo Componente:
export class NewComponent implements OnInit, OnDestroy, SubscriptionGetDataComponent {
readonly subs: Subscription[] = [];
public isLoading = true;

public dataToShow?: DataToShow[];


public otherDataToShow: OtherataToShow[] = [];
public isLoadingOtherDataToShow = true;

constructor(private dataServiceFE: DataServiceFE) {


super();
}

// Inicia as subscrições e chama os dados necessários inicialmente


ngOnInit(): void {
this.initRequests();
this.getData();
}

// Subscrições chamam o dataArrived


initRequests(): void {
this.subs.push(this.dataServiceFE.dataToShow$.subscribe(data => {
this.dataToShow = data;
this.dataArrived();
}),
);

this.subs.push(this.dataServiceFE.otherDataToShow$.subscribe(data => {
this.otherDataToShow = data;
this.isLoadingOtherDataToShow = false;
this.dataArrived();
}),
);
}

getData(): void {
this.databasesService.getDataToShow();
this.databasesService.getOtherDataToShow();
}

// Verifica se os dados já chegaram, ou por boleano ou por variável deixar de ser undefined
dataArrived(): void {
if(this.dataToShow && !this.isLoadingOtherDataToShow){
... funções que dependem destes dados e etc.
this.isLoading = false;
}
}
...
ngOnDestroy(): void {
this.subs.forEach(subscription => {subscription.unsubscribe();
});
}
}

HTML:

<ng-container *ngIf="!isLoading">
...
</ng-container>
Serviço FE
Um serviço de FE para estas páginas, por norma o que faz é a ligação aos pedidos de Swagger para o BE. Pelo que o que vai ter
vão ser os vários métodos que têm de ser feitos, create, updates, gets, deletes e etc. E vai ter os várias váriáveis do tipo subjects,
são estas as subscrições que os componentes estarão à escuta.

Exemplo Serviço:

export class DataServiceFE {


public dataToShow$: Subject<DataToShow[]> = new Subject<DataToShow[]>();
public otherDataToShow$: Subject<OtherataToShow[]> = new Subject<OtherataToShow[]>();

public createdObject$: Subject<ObjectDTO | false> = new Subject<ObjectDTO | false>();


...
constructor(private dataServiceBE: DataServiceBE) {}

getDataToShow(): void {
this.dataServiceBE.getDataToShow().subscribe({
next: (result: DataToShow) => {
this.dataToShow$.next(result);
},
error: (result: any) => {
console.error(result);
this.dataToShow$.next([]);
},
});
}
...

public createObject(object: ObjectDTO): void {


this.dataServiceBE.createObject({ object }).subscribe({
next: result => {
this.createdObject$.next(result);
...
},
error: result => {
this.createdObject$.next(false);
console.error(result);
...
},
});
}
...
}
Com este exemplo, basicamente quando se chamam os métodos getDataToShow e createObject, dá-se trigger daquele .next o
que por conseguinte dará trigger do que estiver na subscrição do componente.

Por isso, para cada método do BE, deve existir um método num serviço de FE e deve estar associado a um Subject. Que deverá
ter uma subscrição no componente devido com o código que deve fazer assim que é recebida uma resposta do BE.

No caso de gets provávelmente será mostrar uma tabela ou um formulário de detalhe.


No caso de creates, updates ou deletes provavelmente será redirecionar para uma outra página ou atualizar uma tabela dessa
alteração.
Componentes Dinâmicos, Componentes
Específicos e Interfaces
Componentes
Código deve ser separado por componentes para tentar simplificar as páginas.

Quando possível devem ser usados os componentes dinâmicos já existentes, sendo que estes podem sempre ser
adaptados caso haja a necessidade.

Mesmo sem serem criados componentes dinâmicos, podem ser criados componentes especificos para aquela página,
consoante a sua separação em secções.

A organização dos componentes também deve estar bem estruturada, pelo que componentes dinâmicos devem estar nas
pastas shared, enquanto que componentes mais específicos de certas páginas devem estar junto dessas pastas.

Interfaces
Código deve sempre tentar estar tipificado pois facilita também a deteção e validação de erros.

Ao criar novos componentes provalvemente será necessário também criar interfaces.

Interfaces podem extender outras e podem também ser usadas para ter a certeza que um certo componente está a ser
criado da maneira correta, como são aquele caso das interfaces SubscriptionComponent.

Interfaces também devem estar bem organizadas. Interfaces específicas de um só componente seja este dinâmico ou
específico devem estar junto desse componente.
Caso faça sentido no shared também podem interfaces dos componentes do shared numa pasta partilhada.

Funções de TS
Se existirem funções de TS que sejam mais gerais ou que faça sentido estas serem partilhadas, estas devem ir para um
local shared, por exemplo em shared -> common.

Deve-se verificar antes de criar uma nova função se já existe alguma partilhada que faça o pretendido, para evitar a
duplicação de código.

Sempre que possível, separar este tipo de funções mais extensas dos ficheiros de ts dos componentes, para simplificar o
código.

Você também pode gostar