AngularJS Frontend#
Visão Geral#
O frontend da aplicação UNICEF Portugal é uma Single Page Application (SPA) construída com AngularJS 1.x. Não é um processo separado -- é HTML e JavaScript estático servido a partir do mesmo Azure App Service que aloja o Umbraco.
Como é Carregado#
O AngularJS não corre separadamente do Umbraco. É carregado através de um template Razor do Umbraco chamado StartView.cshtml (Web/Views/StartView.cshtml).
Este template:
- Herda de
UmbracoTemplatePage(é um template Umbraco normal) - Carrega o nó da homepage para obter o ID do Google Analytics
- Renderiza a página HTML completa com
<html ng-app="unicef"> - Inclui os ficheiros CSS e JS da aplicação Angular (versão minificada)
- Define a estrutura de layout: sidebar, header,
<main ui-view>, footer
Quando o Umbraco recebe um pedido para qualquer URL, serve este template. O AngularJS então "toma conta" da navegação no browser, fazendo chamadas à API para obter o conteúdo de cada página.
Umbraco (StartView.cshtml)
└── Serve HTML com ng-app="unicef"
└── AngularJS faz bootstrap
└── ui-router intercepta navegação
└── Chama /umbraco/Api/ContentApi/GetData/
└── Renderiza template de /Assets/ngviews/
Ficheiros Fonte#
- Desenvolvimento:
Templates/app/index.html(versão não minificada, para referência) - Produção: O template Umbraco
StartView.cshtmlcarrega/Assets/js/main.min.jse/Assets/css/main.min.css - IIS Express (local): Porta SSL 44388 (
https://localhost:44388)
Routing#
A aplicação utiliza angular-ui-router com HTML5 mode:
$locationProvider.html5Mode(true);
Existe um único estado chamado "all" com uma rota catch-all:
URL: *myPath?q
Este estado intercepta toda a navegação do browser.
Fluxo de Carregamento#
sequenceDiagram
participant B as Browser
participant R as ui-router
participant C as Controller
participant API as ContentApiController
participant T as /Assets/ngviews/
B->>R: Navegacao para /qualquer-pagina
R->>R: Estado "all" intercepta (*myPath)
R->>API: Resolve: GET /umbraco/Api/ContentApi/GetData/?url=/qualquer-pagina
API-->>R: JSON {data: {...}, meta: {template: "/Assets/ngviews/xyz.html"}}
R->>C: Controller recebe dados
C->>C: Processa: image crops, template URL, share links
C->>T: ng-include carrega template
T-->>B: Template renderizado com dados
Ciclo de Vida de um Pedido#
- O utilizador navega para um URL (ex:
/noticias/artigo-exemplo) - O
ui-routerintercepta via catch-all*myPath - No
resolvedo estado, é feita uma chamada à API:GET /umbraco/Api/ContentApi/GetData/?url=/noticias/artigo-exemplo - A API retorna JSON com dois blocos:
data: título, corpo, imagem, e outros campos do conteúdometa: caminho do template (ex:"/Assets/ngviews/newsDetail.html")- O controller processa a resposta: define crops de imagens, URL do template, links de partilha
- O template e carregado via
ng-include:<div ng-include="vm.pageData.templateUrl"></div>
Estrutura de Layout#
graph TB
subgraph "Layout da Pagina"
A["sidebar.html - ng-include"]
B["header.html - ng-include"]
C["main - ui-view - conteudo dinamico"]
D["footer.html - ng-include"]
end
A --- B
B --- C
C --- D
Os componentes de layout são carregados via ng-include:
sidebar.html- Barra lateralheader.html- Cabeçalho- main (
ui-view) - Conteúdo dinâmico, muda com cada página footer.html- Rodapé
Templates#
Os templates estão organizados em /Assets/ngviews/:
Assets/ngviews/
components/ -- Componentes reutilizáveis
layout/ -- Estrutura de layout (header, footer, sidebar)
*.html -- Templates de pagina (newsDetail, articleDetail, etc.)
Configuração#
No Web.config existe a seguinte definição:
<add key="Template.Path" value="Assets/ngviews/" />
Esta configuração define o caminho base para os templates do frontend.
Versões: app vs dist#
| Pasta | Uso | Descrição |
|---|---|---|
Templates/app/ |
Desenvolvimento | Código fonte original, não minificado |
Templates/dist/ |
Produção | Build de produção, ficheiros minificados e concatenados |
Impacto na Performance
Se o ContentApiController estiver lento (por exemplo, devido ao excesso de dados na base de dados com ~8M de registos na tabela icUrlTracker, confirmado em produção a 2026-04-14), o spinner de carregamento fica visível indefinidamente. Isto acontece porque o AngularJS aguarda a resposta completa da API antes de renderizar qualquer conteúdo na página. Não existe timeout nem fallback -- a página simplesmente não carrega.