Plataformas de pesquisa SaaS, como SurveyMonkey, Typeform e Google Forms, tornaram a criação de pesquisas acessível a muitos usuários. No entanto, estas ferramentas também introduzem diversas limitações que afetam a flexibilidade e o controle de dados.
Os problemas comuns incluem limites no número de respostas, opções restritas para personalização da interface, opções limitadas de estilo de formulário, dependência de serviços de terceiros para o armazenamento de informações confidenciais e custos crescentes de assinatura que podem se tornar difíceis de sustentar para organizações em crescimento.
Para organizações que desejam automatizar seus processos de coleta de dados, mas ainda exigem controle total sobre seus fluxos de trabalho de dados, uma solução personalizada de gerenciamento de formulários oferece vantagens claras.
Um sistema auto-hospedado garante propriedade completa dos dados e da infraestrutura, personalização irrestrita para diferentes casos de uso, custos operacionais previsíveis sem limitações baseadas no uso e integração direta com sistemas internos. Quando usados juntos, Angular 17 e SurveyJS oferecem uma pilha de tecnologia confiável para construir uma plataforma de gerenciamento de formulários personalizada que excede os recursos de qualquer solução SaaS comercial.
A arquitetura descrita aqui é baseada em três componentes principais:
- Um front-end Angular moderno que usa componentes independentes.
- Um construtor visual da SurveyJS (GitHub) para criar formulários e pesquisas.
- Um back-end PHP simples responsável pelas operações de dados. Você está se referindo a mudar alguma coisa no artigo?
Essa combinação permite construir uma plataforma escalável e de fácil manutenção que mantém todos os aspectos do gerenciamento de formulários totalmente sob seu controle.
Configuração do ambiente: arquitetura técnica e dependências
Estrutura e Dependências do Projeto
O objetivo deste artigo não é fornecer grandes quantidades de código, mas sim explicar como um sistema deste tipo pode ser organizado. O o código-fonte está disponível separadamente para que você possa revisá-lo detalhadamente enquanto segue a explicação estrutural.
Para começar, configuramos um projeto Angular 17 e instalamos as dependências SurveyJS necessárias. A arquitetura da aplicação segue uma estrutura modular que separa as principais etapas do fluxo de trabalho de gerenciamento de formulários:
- Construção de formulário e pesquisa
- Renderização e visualização de formulários
- Processamento e análise de resultados de pesquisas
Crie o projeto Angular
ng new survey1 --standalone --routing --style=scss
cd survey1
Angular 17 introduz melhorias significativas no gerenciamento de componentes autônomos, eliminando a necessidade de NgModules e simplificando a arquitetura geral do aplicativo. Isso resulta em uma estrutura mais limpa e de fácil manutenção.
Instale pacotes SurveyJS
Instale os principais pacotes SurveyJS:
npm install survey-creator-angular survey-angular-ui
npm install @types/survey-creator --save-dev
Configurar o back-end PHP
Crie um api diretório e configure a API PHP:
mkdir api
cd api
Para desenvolvimento local, use o servidor PHP integrado:
php -S localhost:8080
Crie os arquivos PHP necessários para seus endpoints de API. Esses arquivos cuidarão da criação, armazenamento e coleta de resultados de pesquisas.
Estrutura Completa do Projeto
Para ilustrar claramente a aplicação, a seção seguinte mostra como o projeto está organizado.
Você pode revisar o código-fonte para uma compreensão mais detalhada de cada parte. Nossa ideia principal pode ser assim:

Interface visual e interatividade do componente Survey Creator
Implementação do SurveyCreator
O Survey Creator é o componente central da plataforma. Ele permite que os usuários criem pesquisas por meio de uma interface visual. Angular 17 oferece a base estrutural necessária para integrar este componente de maneira estável e previsível.
A interface do construtor de formulários suporta a construção de pesquisas por meio de ações de arrastar e soltar, configuração de lógica condicional, definição de regras de validação e ajuste de configurações visuais. SurveyJS Creator produz automaticamente o esquema JSON que representa a estrutura de cada formulário.
Esse esquema JSON torna-se parte do fluxo de trabalho de gerenciamento de formulários do sistema, porque é armazenado no back-end e posteriormente usado para tarefas de automação de formulários, como renderização de formulários, atualização e visualização e análise de dados de pesquisa.

Configuração de componentes angulares
Com o Angular 17 e seus componentes independentes, a configuração é significativamente simplificada:
// src/app/survey-creator/survey-creator.component.ts
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SurveyCreatorModel } from 'survey-creator-core';
import { SurveyCreatorModule } from 'survey-creator-angular';
import { SurveyService } from '../survey.service';
@Component({
selector: 'app-survey-creator',
standalone: true,
imports: (CommonModule, SurveyCreatorModule),
template: `
Survey Creator
Use the visual editor below to design your survey.
Drag questions from the left panel, configure them, and click "Save Survey" when done.
{{ saving ? 'Saving... ' : 'Save Survey ' }}
{{ loading ? 'Loading...' : 'Load Survey ' }}
{{ message }}
`,
styles: (`
.creator-container {
padding: 20px;
}
h2 {
color: #1976d2;
margin-bottom: 10px;
}
.instructions {
color: #666;
margin-bottom: 15px;
line-height: 1.6;
}
.creator-wrapper {
margin: 20px 0;
border: 1px solid #ddd;
border-radius: 4px;
overflow: hidden;
}
.actions {
display: flex;
gap: 10px;
margin-top: 20px;
}
button {
padding: 12px 24px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
transition: background-color 0.3s;
}
button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.save-btn {
background-color: #4caf50;
color: white;
}
.save-btn:hover:not(:disabled) {
background-color: #45a049;
}
.load-btn {
background-color: #2196f3;
color: white;
}
.load-btn:hover:not(:disabled) {
background-color: #1976d2;
}
.message {
margin-top: 15px;
padding: 12px;
border-radius: 4px;
}
.message.success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.message.error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
`)
})
export class SurveyCreatorComponent implements OnInit {
creator!: SurveyCreatorModel;
saving = false;
loading = false;
message="";
messageType: 'success' | 'error' = 'success';
constructor(private surveyService: SurveyService) {}
ngOnInit() {
// Initialize Survey Creator
// Inicializar el Creador de Encuestas
this.creator = new SurveyCreatorModel({
showLogicTab: true,
showJSONEditorTab: true,
showTestSurveyTab: true,
showDesignerTab: true
});
// Optional: Set default JSON
// Opcional: Establecer JSON por defecto
this.creator.JSON = {
pages: (
{
name: "page1",
elements: (
{
type: "text",
name: "question1",
title: "What is your name?"
}
)
}
)
};
}
/**
* Save survey to backend
* Guardar encuesta en el backend
*/
saveSurvey() {
this.saving = true;
this.message="";
const surveyJson = this.creator.JSON;
this.surveyService.saveSurvey(surveyJson).subscribe({
next: (response) => {
this.saving = false;
if (response.success) {
this.showMessage('Survey saved successfully!', 'success');
} else {
this.showMessage('Error saving survey', 'error');
}
},
error: (error) => {
this.saving = false;
console.error('Error saving survey:', error);
this.showMessage('Error: ' + (error.message || 'Failed to save'), 'error');
}
});
}
/**
* Load survey from backend
* Cargar encuesta desde el backend
*/
loadSurvey() {
this.loading = true;
this.message="";
this.surveyService.loadSurvey().subscribe({
next: (survey) => {
this.loading = false;
if (survey && Object.keys(survey).length > 0) {
this.creator.JSON = survey;
this.showMessage('Survey loaded successfully! ', 'success');
} else {
this.showMessage('No survey found ', 'error');
}
},
error: (error) => {
this.loading = false;
console.error('Error loading survey:', error);
this.showMessage('Error: ' + (error.message || 'Failed to load '), 'error');
}
});
}
private showMessage(text: string, type: 'success' | 'error') {
this.message = text;
this.messageType = type;
if (type === 'success') {
setTimeout(() => {
this.message="";
}, 3000);
}
}
}
Esquemas JSON: Estrutura e Lógica Condicional
Fundamentos do esquema SurveyJS
O esquema JSON produzido pelo SurveyJS Creator representa toda a estrutura, conteúdo e comportamento de uma pesquisa. Ele contém todas as informações sobre as perguntas, páginas e elementos da pesquisa, bem como qualquer lógica condicional que determine como as perguntas são mostradas ou ocultadas com base nas respostas do usuário. Usando este esquema, é possível criar pesquisas simples e complexas sem escrever código adicional.
A seção a seguir fornece um exemplo de esquema JSON gerado pelo SurveyJS Creator:
{
"title": "Customer Satisfaction Survey",
"description": "Help us improve our services",
"pages": (
{
"name": "page1",
"elements": (
{
"type": "rating",
"name": "question1",
"title": "How satisfied are you with our service?",
"autoGenerate": false,
"rateCount": 2,
"rateValues": (
{ "value": 1, "text": "Not at all satisfied" },
{ "value": 2, "text": "Very satisfied" }
),
"rateMax": 2
}
)
}
)
}
Também podemos ver isso visualmente desta forma:

Renderização e experiência do usuário
Implementação do SurveyViewer
O visualizador de pesquisas é o componente que exibe pesquisas aos usuários. Ele garante que as pesquisas sejam renderizadas de forma responsiva, adaptando-se a diferentes dispositivos e tamanhos de tela. A combinação de Angular 17 e SurveyJS permite que o visualizador da pesquisa tenha um desempenho eficiente e permite que você forneça uma experiência consistente e fácil de usar em desktops, tablets e dispositivos móveis.
A seção a seguir mostra um exemplo do visualizador de pesquisa exibindo uma pesquisa carregada:

E este é o código do componente do nosso visualizador:
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SurveyModel } from 'survey-core';
import { SurveyModule } from 'survey-angular-ui';
import { SurveyService, SurveySchema } from '../survey.service';
@Component({
selector: 'app-survey-viewer',
standalone: true,
imports: (CommonModule, SurveyModule),
template: `
Survey Viewer / Visor de Encuestas
Complete the survey below. All fields are required unless otherwise noted.
Completa la encuesta a continuación. Todos los campos son obligatorios a menos que se indique lo contrario.
Loading survey... / Cargando encuesta...
{{ error }}
{{ message }}
`,
styles: (`
.viewer-container {
padding: 20px;
}
h2 {
color: #1976d2;
margin-bottom: 10px;
}
.instructions {
color: #666;
margin-bottom: 15px;
line-height: 1.6;
}
.loading {
text-align: center;
padding: 40px;
color: #666;
font-size: 18px;
}
.error-message {
padding: 15px;
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
border-radius: 4px;
margin: 20px 0;
}
.survey-wrapper {
margin: 20px 0;
}
.message {
margin-top: 15px;
padding: 12px;
border-radius: 4px;
}
.message.success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.message.error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
`)
})
export class SurveyViewerComponent implements OnInit {
survey: SurveyModel | null = null;
loading = false;
error="";
message="";
messageType: 'success' | 'error' = 'success';
constructor(private surveyService: SurveyService) {}
ngOnInit() {
this.loadSurvey();
}
/**
* Load survey from backend
* Cargar encuesta desde el backend
*/
loadSurvey() {
this.loading = true;
this.error="";
this.message="";
this.surveyService.loadSurvey().subscribe({
next: (response: any) => {
this.loading = false;
const surveyJson = response.survey || response;
if (surveyJson && Object.keys(surveyJson).length > 0) {
this.survey = new SurveyModel(surveyJson);
this.setupSurveyCompleteHandler();
console.log('Survey loaded:', surveyJson);
} else {
this.error="No survey found. Please create one first. / No se encontró encuesta. Por favor crea una primero.";
}
},
error: (error) => {
this.loading = false;
console.error('Error loading survey:', error);
this.error="Error loading survey. / Error al cargar encuesta.";
}
});
}
/**
* Setup survey complete handler after survey is loaded
* Configurar el manejador de finalización según la forma oficial de SurveyJS
* Official docs:
*/
private setupSurveyCompleteHandler() {
if (this.survey) {
// Forma oficial de SurveyJS según la documentación
this.survey.onComplete.add((sender) => {
const results = sender.data;
console.log('Survey completed:', results);
this.surveyService.saveResults(results).subscribe({
next: (response) => {
if (response.success) {
this.showMessage('Thank you! Your responses have been saved. / ¡Gracias! Tus respuestas han sido guardadas.', 'success');
} else {
this.showMessage('Error saving results / Error al guardar resultados', 'error');
}
},
error: (error) => {
console.error('Error saving results:', error);
this.showMessage('Error: ' + (error.message || 'Failed to save / Error al guardar'), 'error');
}
});
});
}
}
private showMessage(text: string, type: 'success' | 'error') {
this.message = text;
this.messageType = type;
if (type === 'success') {
setTimeout(() => {
this.message="";
}, 5000);
}
}
}
Temas e personalização visual
SurveyJS fornece um sistema temático que permite a personalização total da aparência da pesquisa. Temas personalizados podem ser usados para manter uma identidade visual consistente que se alinhe com a marca de uma organização.
Integração Completa: Fluxo de Trabalho e Serviços
Serviços angulares para comunicação de back-end
Em aplicações web modernas que interagem com dados externos, é importante manter uma separação clara entre a interface do usuário e a lógica de back-end. Em esta plataforma de gerenciamento de formuláriosa camada de serviço Angular atua como intermediária entre o front-end do SurveyJS e a API de back-end do PHP. Essa separação mantém o aplicativo modular, testável e mais fácil de manter.
Os serviços Angular gerenciam toda a comunicação com o backend PHP. Eles fornecem uma camada de abstração que simplifica as operações de criação, leitura, atualização e exclusão (CRUD), ao mesmo tempo que lida com a autenticação, se necessário.
O principal serviço deste projeto é SurveyServicelocalizado em src/app/survey.service.ts. Este serviço é responsável por todas as interações cliente-servidor relacionadas às pesquisas. Internamente, ele usa Angular’s HttpClient para se comunicar com endpoints REST definidos no backend PHP, como:
/api/load_survey.php/api/save_survey.php/api/save_results.php
As principais responsabilidades de SurveyService incluem:
- loadSurvey() – Carrega o esquema da pesquisa do backend.
- saveSurvey() – Salva o esquema da pesquisa enviando dados JSON para
save_survey.php. - saveResults() – Armazena as respostas do usuário em
survey_results.json. - loadResults() – Recupera todas as respostas acumuladas da pesquisa para análise.
Este serviço fornece uma interface simples e eficaz para gerenciar todas as operações de pesquisa sem exigir autenticação complexa ou lógica de validação. Ele permite que o front-end interaja com o back-end de forma eficiente, mantendo o código modular e de fácil manutenção.
Arquivo: src/app/survey.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
export interface SurveySchema {
pages?: any();
questions?: any();
(key: string): any;
}
export interface SurveyResult {
timestamp: string;
results: any;
}
@Injectable({
providedIn: 'root'
})
export class SurveyService {
// API URL - Adjust if your Laragon setup uses a different path
private apiUrl="/api";
constructor(private http: HttpClient) {}
/**
* Save survey schema to backend
*/
saveSurvey(survey: SurveySchema): Observable {
return this.http.post(`${this.apiUrl}/save_survey.php`, { survey });
}
/**
* Load survey schema from backend
*/
loadSurvey(): Observable {
return this.http.get(`${this.apiUrl}/load_survey.php`);
}
/**
* Save survey results
*/
saveResults(results: any): Observable {
const data: SurveyResult = {
timestamp: new Date().toISOString(),
results
};
return this.http.post(`${this.apiUrl}/save_results.php`, data);
}
/**
* Load survey results
*/
loadResults(): Observable {
return this.http.get(`${this.apiUrl}/load_results.php`);
}
}
Backend PHP: arquitetura direta e fluxo de comunicação
O back-end como sistema de processamento
O backend PHP cuida de todo o processamento de dados para a plataforma de gerenciamento de formulários. Ele recebe solicitações do frontend Angular, processa-as e retorna respostas estruturadas. O back-end usa um padrão de endpoints diretos, com foco na simplicidade e na funcionalidade imediata, em vez de uma arquitetura complexa de múltiplas camadas.
Camada 1: Interface de Comunicação e Configuração CORS
Implementação Técnica
A primeira camada estabelece a comunicação HTTP e configura políticas de compartilhamento de recursos de origem cruzada (CORS). Esta camada é implementada em quatro arquivos PHP principais, que servem como pontos de entrada para o sistema:
api/load_survey.php– Carregamento do esquema de pesquisa- Define cabeçalhos CORS para desenvolvimento local
- Configura o
Content-Typeparaapplication/json - Valida o método HTTP GET
- Lê
survey_schema.jsondodata/diretório - Verifica a integridade do JSON antes de devolvê-lo
api/save_survey.php– Salvamento de esquema- Define cabeçalhos de segurança e CORS
- Valida o método HTTP POST
- Processa entrada JSON com validação
- Cria automaticamente o
data/diretório se não existir - Grava o arquivo com formatação legível e suporte Unicode
Camada 2: Tratamento de Erros e Validação Básica
Pontos finais de armazenamento
api/save_results.php– Armazenamento de resposta- Valida a estrutura dos dados recebidos
- Lê os resultados existentes para preservar as respostas acumuladas
- Adiciona novas respostas com carimbo de data/hora automático
- Mantém a integridade dos dados existentes
- Retorna o número total de envios
api/load_results.php– Recuperação de resultados- Lida com arquivos inexistentes retornando um array vazio
- Valida a integridade JSON
- Normaliza os dados em um formato de array consistente
- Executa validação de estrutura para dados retornados
Fluxo de processamento direto
Salvando um esquema de pesquisa
Solicitação recebida: POST /api/save_survey.php com esquema JSON de pesquisa
Fluxo passo a passo:
- Interface (
api/save_survey.php)
- Analise cabeçalhos HTTP e configure CORS
- Verifique se o método de solicitação é POST
- Ler dados JSON de
php://input
- Validação (mesmo arquivo)
- Verifique a sintaxe JSON
- Verifique a estrutura necessária (
data('survey')) - Garanta campos não vazios
- Persistência (mesmo arquivo)
- Criar
data/diretório se não existir - Escrever
survey_schema.jsonusandoJSON_PRETTY_PRINT - Confirmação de retorno incluindo informações do arquivo e bytes gravados
Resposta:
{ "success": true, "message": "...", "file": "...", "bytes_written": ... }
Salvando respostas do usuário
Solicitação recebida: POST /api/save_results.php com respostas da pesquisa
Fluxo passo a passo:
- Interface (
api/save_results.php)
- Configurar cabeçalhos HTTP
- Validar método de solicitação
- Processar entrada JSON
- Validação (mesmo arquivo)
- Verifique a estrutura de resultados necessária
- Valide o carimbo de data/hora, criando um se estiver faltando
- Acumulação (mesmo arquivo)
- Ler existente
survey_results.json - Anexar nova resposta
- Preservar respostas anteriores
- Persistência (mesmo arquivo)
- Grave o array atualizado no arquivo
- Resposta de retorno incluindo contagem total de envios
Resposta:
{ "success": true, "message": "...", "total_submissions": ..., "bytes_written": ... }
Fluxo de trabalho e arquitetura completos
A plataforma de pesquisas funciona como um sistema integrado com a seguinte estrutura:
Frontend Angular ←→ PHP API Endpoints ←→ JSON Files (data/)
↑ ↑
SurveyJS Direct Endpoints
Components + survey_schema.json
SurveyService + survey_results.json
CORS Configured
Etapas do fluxo de trabalho:
1. Criação:
Pesquisa de designs de usuários → SurveyCreator → save_survey.php → data/survey_schema.json
2. Visualização:
Carregamentos de aplicativos → load_survey.php → SurveyViewer renderiza pesquisa
3. Coleta de respostas:
O usuário completa a pesquisa → SurveyService.saveResults() → save_results.php → data/survey_results.json
4. Análise:
Consultas de aplicativos → load_results.php → Processamento de matriz de resposta
Conclusão
A combinação do Angular 17 e do SurveyJS fornece uma base estável para a construção de uma plataforma robusta de gerenciamento de formulários. A arquitetura de endpoint direto demonstra que a simplicidade no design não reduz a funcionalidade.
Este sistema lida com a criação de pesquisas, renderização, respostas do usuário e armazenamento de resultados de forma eficiente, mantendo-se fácil de manter. Ele também supera as limitações das ferramentas de pesquisa SaaS, dando às organizações controle total sobre seus dados e a flexibilidade para implementar qualquer personalização na interface do usuário do criador de formulários e nos formulários.
Ao utilizar uma arquitetura simples com endpoints bem definidos, a plataforma alcança funcionalidade prática sem complexidade desnecessária.