DEV Community

Meu Código Ágil
Meu Código Ágil

Posted on

[Off Topic] Nano introdução do framework Angular para Devs do back

Fugindo um pouco do tema do canal, resolvi escrever esse post destinado aos Devs do backend que estão acompanhando a nossa jornada de descoberta da ferramenta JHipster. Caso você tenha caído aqui de paraquedas, trata-se de uma série de posts que começamos aqui.

Esse post não tem nem mesmo a pretensão de ensinar você a fazer um mero “Hello World” em Angular. A intenção aqui é apenas introduzir um conjunto pequeno de conceitos básicos do Angular para que os desenvolvedores que não têm a menor desenvoltura com frontend possam acompanhar menos agoniados as análises de código gerado pelo JHipster.

Durante as descrições dos conceitos abaixo, quando eu fizer referência a “nossa aplicação”, estou me referindo a uma aplicação monolítica exemplo que foi gerada com o JHipster 8 durante essa série de posts que citei acima. O post da geração da aplicação em si é este aqui, mas se preferir, o código também está no Github.

Estando tudo isso esclarecido, vamos começar? :)

Componente

uase tudo é um componente no Angular. Podemos entendê-lo como um código que encapsula HTML, CSS e lógica em Typescript. Não por acaso, tipicamente, um componente no Angular é formado por 3 arquivos: um HTML, um arquivo de estilos (CSS ou SCSS) e um arquivo TS. Tomemos como exemplo o componente app/home da nossa aplicação:

No arquivo home.component.scss, não tem nada além da definição de classes de estilos que vão estipular todas aquelas coisinhas que a galera de UI se amarra: posicionamento, tamanho, fontes, cores … nada que desperte encantos em um Dev do back, mas se for só pra dar uma olhadinha sem compromisso, também não assusta.

.hipster {
  display: inline-block;
  width: 347px;
  height: 497px;
  background: url('../../content/images/jhipster_family_member_3.svg') 
    no-repeat center top;
  background-size: contain;
}
Enter fullscreen mode Exit fullscreen mode

No arquivo home.component.html, como você já deve ter imaginado, tem um monte de tags HTML, textos, referências a classes CSS. Nada que nós backenders não tenhamos visto antes, certo?

<div class="row">
  <div class="col-md-3">
    <span class="hipster img-fluid rounded"></span>
  </div>
  <div class="col-md-9">
    <h1 class="display-4">
      <span jhiTranslate="home.title">Bem vindo, Java Hipster!</span> 
      (Minha Aplicacao Monolitica)
    </h1>
    <p class="lead" jhiTranslate="home.subtitle">
      Esta é a página principal
    </p>  
...
Enter fullscreen mode Exit fullscreen mode

Com exceção de coisinhas como esse @if (....) no trecho a seguir. Mas que, muito provavelmente, você já viu algo parecido em soluções que usavam templates como ASP, JSP etc … é só uma lógica de exibição de um trecho de HTML ou outro que faz referência a uma variável/atributo.

<div>
      @if (account() !== null) {
        <div class="alert alert-success">
          @if (account()) {
            <span id="home-logged-message" jhiTranslate="home.logged.message" [translateValues]="{ username: account()!.login }"
              >Você está logado como &quot;{{ account()!.login }}&quot;.</span
            >
          }
        </div>
      } @else {
        <div class="alert alert-warning">
          <span jhiTranslate="global.messages.info.authenticated.prefix">Se deseja </span>
          <a class="alert-link" (click)="login()" jhiTranslate="global.messages.info.authenticated.link">entrar</a
          ><span jhiTranslate="global.messages.info.authenticated.suffix"
            >, utilize as seguintes contas padrões:<br />- Administrador (usuário=&quot;admin&quot; and senha=&quot;admin&quot;) <br />-
            Usuário (usuário=&quot;user&quot; e senha=&quot;user&quot;).</span
          >
        </div>
        <div class="alert alert-warning">
          <span jhiTranslate="global.messages.info.register.noaccount">Não possui uma conta ainda?</span>&nbsp;
          <a class="alert-link" routerLink="/account/register" jhiTranslate="global.messages.info.register.link">Crie uma nova conta</a>
        </div>
      }
Enter fullscreen mode Exit fullscreen mode

E adivinhe onde está essa variável … exatamente! Está na classe Typescript declarada no arquivo home.component.ts resumida a seguir:

@Component({
  standalone: true,
  selector: 'jhi-home',
  templateUrl: './home.component.html',
  styleUrl: './home.component.scss',
  imports: [SharedModule, RouterModule],
})
export default class HomeComponent implements OnInit, OnDestroy {

  account = signal<Account | null>(null);
  ...

  login(): void {
    this.router.navigate(['/login']);
  }
  ...
}
Enter fullscreen mode Exit fullscreen mode

Perceba que a classe possui uma anotação @Component onde dois dos atributos que a anotação recebe são exatamente o path relativo dos arquivos HTML e SCSS. Outro atributo que cabe chamarmos atenção é o selector. Esse atributo define qual será a tag desse componente HomeComponent que o JHipster criou pra nós. Isso significa que poderíamos utilizar este componente dentro de outro componente utilizando a tag jhi-home como neste exemplo hipotético:

<div>
  <jhi-home></jhi-home>
</div>
Enter fullscreen mode Exit fullscreen mode

Você vai encontrar no mesmo diretório o arquivo home.component.spec.ts. Por ora, basta saber que se trata dos testes unitários do componente.

Um último ponto para fecharmos a explicação sobre componentes Angular: um componente não precisa necessariamente ter o arquivo de estilos (CSS ou SCSS). Veja, por exemplo, o componente app/login da nossa aplicação:

@Component({
  selector: 'jhi-login',
  standalone: true,
  imports: [SharedModule, FormsModule, ReactiveFormsModule, RouterModule],
  templateUrl: './login.component.html',
})
export default class LoginComponent implements OnInit, AfterViewInit {
  @ViewChild('username', { static: false })
  username!: ElementRef;
  ...
Enter fullscreen mode Exit fullscreen mode

Além de não ter um arquivo de estilos no diretório login, perceba que na anotação @Component o atributo styleUrl também não é setado. Mas olhando o seu template, o arquivo HTML, é possível notar referências a classes de estilo. Entretanto, essas classes vêm da pacote Bootstrap ou de algum dos arquivos da pasta webapp/content/scss.

<div class="d-flex justify-content-center">
   <div class="col-lg-6 col-md-8 col-sm-10">
     <h1 jhiTranslate="login.title" data-cy="loginTitle">Autenticação</h1>
     @if (authenticationError()) {
       <div class="alert alert-danger" jhiTranslate="login.messages.error.authentication" data-cy="loginError">
         <strong>Erro de autenticação!</strong> Por favor verifique suas credenciais e tente novamente.
       </div>
     }
     <form class="form" role="form" (ngSubmit)="login()" [formGroup]="loginForm">
  ...
Enter fullscreen mode Exit fullscreen mode

Serviço

Aqui está um conceito que não vai deixar nenhum Dev backend desconfortável. Afinal, um Serviço é tão somente uma classe Typescript que fornece funcionalidades específicas para serem reutilizadas por distintas partes da aplicação. Não tem HTML, não tem CSS … é tão somente: CÓ-DI-GO!

Veja, por exemplo, o serviço app/core/auth/state-storage.service.ts que, basicamente, encapsula funcionalidades de ler, escrever e apagar dados na sessionStorage do navegador.

@Injectable({ providedIn: 'root' })
export class StateStorageService {
  private previousUrlKey = 'previousUrl';
  private authenticationKey = 'jhi-authenticationToken';
  private localeKey = 'locale';

  storeUrl(url: string): void {
    sessionStorage.setItem(this.previousUrlKey, JSON.stringify(url));
  }

  getUrl(): string | null {
    const previousUrl = sessionStorage.getItem(this.previousUrlKey);
    return previousUrl ? (JSON.parse(previousUrl) as string | null) : previousUrl;
  }

  clearUrl(): void {
    sessionStorage.removeItem(this.previousUrlKey);
  }
Enter fullscreen mode Exit fullscreen mode

Sabe outro tipo de funcionalidade que também fica a cargo de Serviços? Requisições HTTP a APIs! Veja abaixo um trecho do serviço app/entities/produto/service/produto.service.ts:

@Injectable({ providedIn: 'root' })
export class ProdutoService { 

  protected http = inject(HttpClient);
  protected applicationConfigService = inject(ApplicationConfigService);

  protected resourceUrl = this.applicationConfigService.getEndpointFor('api/produtos');

  create(produto: NewProduto): Observable<EntityResponseType> {
    return this.http.post<IProduto>(this.resourceUrl, produto, { observe: 'response' });
  }

  ...
  find(id: number): Observable<EntityResponseType> {
    return this.http.get<IProduto>(`${this.resourceUrl}/${id}`, { observe: 'response' });
  }

  ...
Enter fullscreen mode Exit fullscreen mode

Apenas para ficar mais claro, selecionei um método do componente app/entities/produto/update/produto-update.component.ts que mostra o método create do nosso ProdutoService sendo utilizado:

@Component({
  standalone: true,
  selector: 'jhi-produto-update',
  templateUrl: './produto-update.component.html',
  imports: [SharedModule, FormsModule, ReactiveFormsModule],
})
export class ProdutoUpdateComponent implements OnInit {

  protected produtoService = inject(ProdutoService);

  ...
  save(): void {
    this.isSaving = true;
    const produto = this.produtoFormService.getProduto(this.editForm);
    if (produto.id !== null) {
      this.subscribeToSaveResponse(this.produtoService.update(produto));
    } else {
      this.subscribeToSaveResponse(this.produtoService.create(produto));
    }
  }
...
Enter fullscreen mode Exit fullscreen mode

Interceptador

Mais um treco que não vai assustar nenhum Dev do back, afinal, é só código também. Um interceptador tem o papel de interceptar e modificar requisições HTTP, tem função semelhante a dos filters da API Servlet do Java.

Com um interceptor, você pode por exemplo, inserir um cabeçalho Authorization em todas as requisições destinadas aos endpoints do backend. Se você abrir o interceptador app/core/interceptor/auth-interceptor.ts, vai constatar que é exatamente isso que ele está fazendo na implementação do método intercept:

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  private stateStorageService = inject(StateStorageService);
  private applicationConfigService = inject(ApplicationConfigService);

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const serverApiUrl = this.applicationConfigService.getEndpointFor('');
    if (!request.url || (request.url.startsWith('http') && !(serverApiUrl && request.url.startsWith(serverApiUrl)))) {
      return next.handle(request);
    }

    const token: string | null = this.stateStorageService.getAuthenticationToken();
    if (token) {
      request = request.clone({
        setHeaders: {
          Authorization: `Bearer ${token}`,
        },
      });
    }
    return next.handle(request);
  }
}
Enter fullscreen mode Exit fullscreen mode

Rota

Uma rota define como o Angular responderá a diferentes URLs. Em outras palavras, uma rota define qual componente Angular será apresentado para um caminho de URL específico. Simples assim!

const routes: Routes = [
  {
    path: '',
    component: HomeComponent,
  },
  {
    path: 'login',
    component: LoginComponent,
  },
];
Enter fullscreen mode Exit fullscreen mode

No exemplo acima, quando o usuário digitar no navegador http://localhost:4200/, o componente exibido pelo Angular será o HomeComponent. Já quando ele digitar ou for redirecionado para http://localhost:4200/login, é o LoginComponent que será exibido na interface do usuário.

Além de declarar as rotas da aplicação, elas precisam ser registradas para que a aplicação tenha o comportamento esperado.

As rotas que coloquei acima como exemplo estão declaradas da forma mais simples possível. Uma rota pode ser bem mais complexa que isso, ela pode por exemplo:

  • definir dados a serem enviados para o componente especificado na rota;
  • carregar outro conjunto de rotas dinamicamente;
  • implementar lógica a ser executada antes do carregamento do componente da rota. Por exemplo, para validar se o usuário tem as roles de acesso necessárias;
  • implementar lógica a ser executada antes de sair do componente da rota. Por exemplo, para pedir uma confirmação do usuário caso ele esteja abandonando um formulário no meio de uma edição sem salvar os dados.

Enfim … uma rota nos dá muitas possibilidades, mas acho que para a nano introdução do Angular à turma do backend que pretendíamos, podemos parar por aqui antes de causarmos uma repulsa indesejada rs

Mas se você chegou até aqui e ficou interessado em se aprofundar mais no assunto, o que não falta é gente gabaritada por aí ensinando Angular, até mesmo de graça. E, na boa? Aprender nunca é demais. Ainda que você não tenha intenção de (re)direcionar a sua carreira para o frontend, o conhecimento sobre o framework vai agregar muito na sua interação com os colegas responsáveis por esse nicho nos seus projetos.

Duas fontes que me ajudaram a entender melhor esse mundo (sim, apenas entender, eu não fui convertido rsrs) foram: o canal da Loiane Groner no YouTube e a Formação Angular na Alura. Sem falar, claro, nos próprios canais oficiais do Angular: o blog e o canal no YouTube.

Top comments (0)