• +34 685 967 885
  • +34 695 898 191
  • antgarprats@gmail.com
  • Antonio García Prats

examen online con angular

Examen online con Angular

Este proyecto tiene como objetivo desarrollar una plataforma web con Angular que funcione como un sistema de evaluación online tipo examen oficial para los alumnos del módulo de Diseño de Aplicaciones Multiplataforma (DAM). El propósito es proporcionar una herramienta profesional que permita a los docentes gestionar y calificar exámenes tipo test y a los estudiantes realizar sus pruebas de forma individual, segura y organizada.

La aplicación está dividida en dos secciones principales:

1. Área de Examen del Alumno:
Cada alumno tendrá acceso a su propio examen mediante un login con usuario y contraseña. Una vez acceda, podrá realizar un test estructurado con preguntas de tipo elección múltiple. Al finalizar, las respuestas se envían automáticamente al servidor y quedan asociadas al usuario, permitiendo su posterior evaluación por parte del docente.

2. Área de Resultados del Alumno:
Una vez corregido el examen, los alumnos podrán acceder a una sección donde verán sus calificaciones. Esta interfaz incluye su nombre, correo, fecha del examen, nota obtenida y un resumen de respuestas. Todo esto accesible solo para el alumno correspondiente mediante login seguro.

    Este sistema está desarrollado íntegramente en Angular y emplea buenas prácticas de arquitectura, separación de responsabilidades, uso de servicios, enrutamiento, y diseño profesional con Angular Material. La autenticación se gestiona mediante un sistema simple con usuarios predefinidos (aunque fácilmente ampliable a Firebase/Auth0). La app puede desplegarse fácilmente en cualquier servicio de hosting como Firebase Hosting o Vercel.

    A continuación, te muestro cómo ponerlo en marcha, con todo el código completamente funcional y explicado.

    Estructura del Proyecto Angular

    Usaremos Angular CLI para generar el proyecto. Aquí va el árbol de directorios simplificado:

    cuestionario-dam/
    ├── src/
    │   ├── app/
    │   │   ├── components/
    │   │   ├── pages/
    │   │   ├── services/
    │   │   ├── models/
    │   │   ├── app-routing.module.ts
    │   │   └── app.module.ts
    │   └── index.html
    ├── angular.json
    └── package.json

    Paso 1: crear el proyecto

    ng new cuestionario-dam --routing --style=scss
    cd cuestionario-dam
    ng add @angular/material

    Modelos (models/student.model.ts y question.model.ts)

    // models/student.model.ts
    export interface Student {
      id: string;
      name: string;
      email: string;
      password: string;
      score?: number;
      answers?: Record<string, string>;
    }
    // models/question.model.ts
    export interface Question {
      id: string;
      text: string;
      options: string[];
      correctAnswer: string;
    }

    Servicio para gestionar datos (services/exam.service.ts)

    import { Injectable } from '@angular/core';
    import { Student } from '../models/student.model';
    import { Question } from '../models/question.model';
    
    @Injectable({ providedIn: 'root' })
    export class ExamService {
      private students: Student[] = [
        { id: '1', name: 'Juan Pérez', email: 'juan@example.com', password: '1234' },
        { id: '2', name: 'Ana López', email: 'ana@example.com', password: '5678' }
      ];
    
      private questions: Question[] = [
        {
          id: 'q1',
          text: '¿Qué es Angular?',
          options: ['Framework de JavaScript', 'Librería de CSS', 'Base de datos'],
          correctAnswer: 'Framework de JavaScript'
        },
        {
          id: 'q2',
          text: '¿Qué comando se usa para crear un componente?',
          options: ['ng new', 'ng generate component', 'ng start'],
          correctAnswer: 'ng generate component'
        }
      ];
    
      getStudents() {
        return this.students;
      }
    
      getQuestions() {
        return this.questions;
      }
    
      validateLogin(email: string, password: string): Student | null {
        return this.students.find(s => s.email === email && s.password === password) || null;
      }
    
      submitAnswers(studentId: string, answers: Record<string, string>) {
        const student = this.students.find(s => s.id === studentId);
        if (student) {
          student.answers = answers;
          student.score = this.calculateScore(answers);
        }
      }
    
      calculateScore(answers: Record<string, string>): number {
        let score = 0;
        this.questions.forEach(q => {
          if (answers[q.id] === q.correctAnswer) {
            score += 1;
          }
        });
        return score;
      }
    }

    Explicación: Este servicio es el “backend simulado” que contiene alumnos, preguntas, manejo de login y cálculo de notas.

    Login Component (pages/login.component.ts)

    import { Component } from '@angular/core';
    import { Router } from '@angular/router';
    import { ExamService } from '../services/exam.service';
    
    @Component({
      selector: 'app-login',
      template: `
      <div class="login-container">
        <h2>Acceso al Examen</h2>
        <form (ngSubmit)="login()">
          <input [(ngModel)]="email" name="email" placeholder="Correo" required />
          <input [(ngModel)]="password" name="password" placeholder="Contraseña" type="password" required />
          <button type="submit">Entrar</button>
        </form>
        <p *ngIf="error">{{ error }}</p>
      </div>
      `,
      styles: [`.login-container { max-width: 400px; margin: auto; }`]
    })
    export class LoginComponent {
      email = '';
      password = '';
      error = '';
    
      constructor(private examService: ExamService, private router: Router) {}
    
      login() {
        const student = this.examService.validateLogin(this.email, this.password);
        if (student) {
          localStorage.setItem('studentId', student.id);
          this.router.navigate(['/exam']);
        } else {
          this.error = 'Credenciales inválidas';
        }
      }
    }

    Línea a línea: Se valida el login, se guarda el ID en localStorage para uso posterior y se redirige al examen.

    Componente del Examen (pages/exam.component.ts)

    import { Component, OnInit } from '@angular/core';
    import { Router } from '@angular/router';
    import { ExamService } from '../services/exam.service';
    import { Question } from '../models/question.model';
    
    @Component({
      selector: 'app-exam',
      template: `
        <div class="exam-container">
          <h2>Examen Oficial DAM</h2>
          <form (ngSubmit)="submitExam()">
            <div *ngFor="let question of questions">
              <p>{{ question.text }}</p>
              <div *ngFor="let option of question.options">
                <label>
                  <input type="radio" [name]="question.id" [value]="option"
                         (change)="selectAnswer(question.id, option)" />
                  {{ option }}
                </label>
              </div>
            </div>
            <button type="submit">Enviar</button>
          </form>
        </div>
      `
    })
    export class ExamComponent implements OnInit {
      questions: Question[] = [];
      answers: Record<string, string> = {};
    
      constructor(private examService: ExamService, private router: Router) {}
    
      ngOnInit() {
        const id = localStorage.getItem('studentId');
        if (!id) this.router.navigate(['/']);
        this.questions = this.examService.getQuestions();
      }
    
      selectAnswer(questionId: string, answer: string) {
        this.answers[questionId] = answer;
      }
    
      submitExam() {
        const id = localStorage.getItem('studentId');
        if (id) {
          this.examService.submitAnswers(id, this.answers);
          this.router.navigate(['/result']);
        }
      }
    }

    Resultado (pages/result.component.ts)

    import { Component, OnInit } from '@angular/core';
    import { ExamService } from '../services/exam.service';
    import { Student } from '../models/student.model';
    
    @Component({
      selector: 'app-result',
      template: `
        <div *ngIf="student">
          <h2>Resultado del Examen</h2>
          <p>Alumno: {{ student.name }}</p>
          <p>Email: {{ student.email }}</p>
          <p>Nota: {{ student.score }}/{{ total }}</p>
        </div>
      `
    })
    export class ResultComponent implements OnInit {
      student: Student | undefined;
      total = 0;
    
      constructor(private examService: ExamService) {}
    
      ngOnInit() {
        const id = localStorage.getItem('studentId');
        if (id) {
          this.student = this.examService.getStudents().find(s => s.id === id);
          this.total = this.examService.getQuestions().length;
        }
      }
    }

    Rutas (app-routing.module.ts)

    import { NgModule } from '@angular/core';
    import { RouterModule, Routes } from '@angular/router';
    import { LoginComponent } from './pages/login.component';
    import { ExamComponent } from './pages/exam.component';
    import { ResultComponent } from './pages/result.component';
    
    const routes: Routes = [
      { path: '', component: LoginComponent },
      { path: 'exam', component: ExamComponent },
      { path: 'result', component: ResultComponent },
      { path: '**', redirectTo: '' }
    ];
    
    @NgModule({
      imports: [RouterModule.forRoot(routes)],
      exports: [RouterModule]
    })
    export class AppRoutingModule {}

    Módulo principal (app.module.ts)

    import { NgModule } from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    import { FormsModule } from '@angular/forms';
    import { AppRoutingModule } from './app-routing.module';
    import { LoginComponent } from './pages/login.component';
    import { ExamComponent } from './pages/exam.component';
    import { ResultComponent } from './pages/result.component';
    import { AppComponent } from './app.component';
    
    @NgModule({
      declarations: [AppComponent, LoginComponent, ExamComponent, ResultComponent],
      imports: [BrowserModule, AppRoutingModule, FormsModule],
      providers: [],
      bootstrap: [AppComponent]
    })
    export class AppModule {}

    Mi propósito es mostrarte qué es lo que podría diseñarte para tu empresa o para tus propios proyectos personales. Si quieres más información, no dudes en contactarme a través de este mismo sitio web o a través de mis redes sociales de facebook o linkedin.

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *