Como construir um calendário mensal - Angular 2+
Particularmente já precisei de um componente que exibisse um calendário mensal. Na época fiz uma busca na internet e não encontrei um componente que atendesse especificamente minha demanda, então resolvi criar um componente e vou compartilhar a ideia geral do componente neste texto.
Desenvolvendo a lógica
Nosso calendário terá o seguinte formato:
Se você focar nos dias que o calendário apresenta, dá para perceber que podemos tratar os dias como um array. Então a primeira posição do array seria no dia 27/03 (primeiro dia exibido na imagem acima) e a última posição do array seria no dia 30/04.
Focando na construção deste array vamos começar com um simples JavaScript tendo como objetivo preencher um array de dias, onde a primeira posição deve ser um domingo (primeiro dia da semana) e a última um sábado (último dia da semana). Então se estamos no mês de abril/2022 podemos considerar que o dia 01/04/22 é uma sexta-feira, então nosso calendário iniciará no dia 27/03/22 (domingo) e terminará no dia 30/04/2022 (sábado) como a imagem acima mostra.
Veja o código abaixo e leia os comentários para acompanhar a lógica:
// Pega a data corrente do computador
// Obs.: Executei este código no mês
// de abr/2022
const dataAtual = new Date();
const ano = dataAtual.getFullYear();
const mes = dataAtual.getMonth();
//
// Em JS os dias da semana começam no domingo
// (dom=0, seg=1, ter=2, ...)
//
const primeiroDiaDaSemana = 0; // domingo
const ultimoDiaDaSemana = 6; // sábado
//
// Cria a data inicial começando no dia 1.
// Vai subtraindo -1 dia até chegarmos no primeiro
// dia da semana
//
const dataInicial = new Date(ano, mes, 1);
while (dataInicial.getDay() !== primeiroDiaDaSemana) {
dataInicial.setDate(dataInicial.getDate() - 1);
}
//
// Cria a data final, último dia do mês, para fazer isso
// é só somar +1 no mês e deixar o dia como 0.
//
// Por exemplo: new Date(2022, 1 /* fevereiro */, 0)
// é igual a 31/01/2022
//
// Vai somando +1 até chegarmos no último dia da semana
//
const dataFinal = new Date(ano, mes + 1, 0);
while (dataFinal.getDay() !== ultimoDiaDaSemana) {
dataFinal.setDate(dataFinal.getDate() + 1);
}
// Vamos preencher o array diasCalendario com um dia em
// cada posição:
const diasCalendario = [];
for (
let data = new Date(dataInicial.getTime());
data <= dataFinal;
data.setDate(data.getDate() + 1)
) {
diasCalendario.push(new Date(data.getTime()));
}
console.table(diasCalendario);
Resultado exibido no console:
Iniciando a construção do componente
Agora que temos um script que preenche um array com as datas do calendário, vamos começar a construção do componente em Angular 2+ com um código bem simples e aos poucos vamos evoluindo:
Estruturando os arquivos:
Trazendo a lógica desenvolvida mais acima temos:
Repare que o construirCalendario()
é chamado no ngOnInit()
, então a variável diasCalendario
contém os dias que nosso calendário deverá exibir. Inicialmente vamos utilizar um template bem simples para renderizar o array de dias, só para ter certeza de que tudo está funcionando:
No resultado abaixo é possível ver que as datas batem com o calendário de abril/2022. Ele inicia em 27/03 (domingo) e finaliza no dia 30/04 (sábado).
Ajustando o grid do calendário
Agora que já temos uma estrutura inicial, vamos focar um pouco na disposição destes dias na tela. Repare na imagem acima, os dias do calendário aparecem um ao lado do outro. Para atingir nosso objetivo, o ideal é que estes dados sejam exibidos de forma tabular, onde temos n
linhas e 7 colunas (uma coluna para cada dia da semana), exatamente como um calendário. Há várias formas de chegarmos neste resultado, mas aqui vou optar em usar o bom e velho CSS. Podemos recorrer ao recurso display: grid
para definir a quantidade de elementos por linha, que no nosso caso serão 7, conforme o código abaixo ilustra:
CSS Grid será suportado por vários navegadores até meados de 2017. O suporte em navegadores antigos pode ser obtido habilitando-se uma flag que permite o uso da API. Nesse caso não se esqueça de consultar e fazer referência a cada propriedade e funcionalidade da especificação para certificar-se da sua compatibilidade, bem como para obter maiores informações.
fonte: https://developer.mozilla.org/pt-BR/docs/Web/CSS/CSS_Grid_Layout
Também será necessário alterar o template para:
Agora sim, o componente já está se parecendo com um calendário de verdade, veja na imagem abaixo:
Melhorando o componente calendário
Vamos adicionar as seguintes melhorias ao nosso componente calendário:
- inclusão de botões de navegação (próximo e anterior) - deste modo o usuário consegue visualizar outros meses.
- inclusão dos labels com os dias da semana (seg, ter, etc) no topo de cada coluna;
- deixar a cor dos textos dos dias que não fazem parte do mês corrente em cinza claro;
- adicionar um título com o nome do mês e ano que o calendário está mostrando
Resultado final:
Considerações
Apesar de ser um componente bem simples sua construção envolveu alguns pontos bem interessantes como:
- uso do
display: grid
do CSS - cálculo envolvendo datas
- formatação de data através do
datePipe
- inclusive já escrevi sobre isto neste link.
Vale comentar que os dias da semana que são mostrados na imagem acima estão em inglês. Para deixar em pt-BR
basta realizar uma configuração descrita neste link.
Link do código fonte: https://stackblitz.com/edit/como-construir-um-calendario-mensal-angular?embed=1&file=src/app/calendario/calendario.component.html&theme=dark