Skip to main content

Overview

The Patient component provides a comprehensive list view of all patients with search functionality, filtering capabilities, and quick actions for creating new appointments and managing patient records.

Component Definition

selector
string
default:"app-patient"
The CSS selector used to include this component in templates
standalone
boolean
default:true
Standalone component with explicit imports
imports
array
Dependencies: CommonModule, FormsModule, RouterModule, NewAppointment

Source Code Location

File: src/app/patient/patient.ts:8
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { PatientService, PatientData } from '../services/patient.service';
import { NewAppointment } from '../new-appointment/new-appointment';

@Component({
  selector: 'app-patient',
  standalone: true,
  imports: [CommonModule, FormsModule, RouterModule, NewAppointment],
  templateUrl: './patient.html',
  styleUrl: './patient.css',
})
export class Patient implements OnInit {
  searchText: string = '';
  patients: PatientData[] = [];
  isNewAppointmentModalOpen: boolean = false;

  constructor(private patientService: PatientService) { }

  ngOnInit(): void {
    this.patients = this.patientService.getPatients();
  }

  get filteredPatients() {
    if (!this.searchText) {
      return this.patients;
    }
    const search = this.searchText.toLowerCase();
    return this.patients.filter(p =>
      (p.nombre?.toLowerCase().includes(search) || false) ||
      (p.email?.toLowerCase().includes(search) || false) ||
      (p.phone?.includes(search) || false)
    );
  }

  openNewAppointmentModal(): void {
    this.isNewAppointmentModalOpen = true;
  }

  closeNewAppointmentModal(): void {
    this.isNewAppointmentModalOpen = false;
  }
}

Properties

searchText

searchText
string
default:"''"
The current search query entered by the user
This property is bound to the search input using two-way binding ([(ngModel)]), automatically updating as the user types. Implementation: src/app/patient/patient.ts:16

patients

patients
PatientData[]
default:"[]"
Array of all patient records loaded from the service
Populated during ngOnInit() by calling patientService.getPatients(). Implementation: src/app/patient/patient.ts:17

isNewAppointmentModalOpen

isNewAppointmentModalOpen
boolean
default:false
Controls the visibility of the new appointment modal
Implementation: src/app/patient/patient.ts:18

Computed Properties

filteredPatients

filteredPatients
PatientData[]
Getter that returns filtered patients based on the search text
Filters patients by matching the search text against:
  • Patient name (nombre)
  • Email address (email)
  • Phone number (phone)
The search is case-insensitive and returns all patients if the search text is empty. Implementation: src/app/patient/patient.ts:26-36
get filteredPatients() {
  if (!this.searchText) {
    return this.patients;
  }
  const search = this.searchText.toLowerCase();
  return this.patients.filter(p =>
    (p.nombre?.toLowerCase().includes(search) || false) ||
    (p.email?.toLowerCase().includes(search) || false) ||
    (p.phone?.includes(search) || false)
  );
}

Methods

ngOnInit()

ngOnInit
() => void
Lifecycle hook that loads patient data when the component initializes
ngOnInit(): void {
  this.patients = this.patientService.getPatients();
}
Implementation: src/app/patient/patient.ts:22-24

openNewAppointmentModal()

openNewAppointmentModal
() => void
Opens the new appointment modal dialog
Implementation: src/app/patient/patient.ts:38-40

closeNewAppointmentModal()

closeNewAppointmentModal
() => void
Closes the new appointment modal dialog
Implementation: src/app/patient/patient.ts:42-44

Template Structure

The component template is organized into several sections:

Header Section

title-section
div
  • Title: “Pacientes”
  • Description: “Gestión de pacientes de la clínica”
actions
div
  • Nueva Cita button: Opens appointment modal
  • Nuevo Paciente button: Navigates to /patient/new

Search Section

Search input with icon and two-way binding to searchText
<input type="text" 
       placeholder="Buscar por nombre, email o teléfono..." 
       [(ngModel)]="searchText">

Table Section

  1. Paciente: Name and age
  2. Contacto: Phone and email with icons
  3. Última Visita: Date of last visit
  4. Próxima Cita: Date of next appointment
  5. Estado: Status badge (activo, pendiente, urgente)
  6. Acciones: View details link
modal-overlay
div
Displays the new appointment modal when isNewAppointmentModalOpen is true
<div class="modal-overlay" *ngIf="isNewAppointmentModalOpen" 
     (click)="closeNewAppointmentModal()">
  <div class="modal-content" (click)="$event.stopPropagation()">
    <app-new-appointment 
      (close)="closeNewAppointmentModal()"
      (created)="closeNewAppointmentModal()">
    </app-new-appointment>
  </div>
</div>

PatientData Interface

The component works with the PatientData interface:
export interface PatientData {
  id: number;
  nombre: string;
  edad: number;
  phone: string;
  email: string;
  address: string;
  medication_allergies: string;
  billing_data: string;
  health_status: string;
  family_history: string;
  ultimaVisita: string;
  proximaCita: string;
  estado: string;
  citas: Cita[];
  tratamientos: Tratamiento[];
}

Service Integration

The component depends on PatientService for data operations:
getPatients()
() => PatientData[]
Retrieves all patient records from the service
constructor(private patientService: PatientService) { }

ngOnInit(): void {
  this.patients = this.patientService.getPatients();
}

Usage Example

The Patient component is typically used as a routed component:
import { Routes } from '@angular/router';
import { Patient } from './patient/patient';

export const routes: Routes = [
  {
    path: 'patient',
    component: Patient,
    data: { title: 'Pacientes' }
  }
];

Status Badge Styling

The status badge uses dynamic CSS classes:
<span class="status-badge" [ngClass]="patient.estado">
  {{ patient.estado }}
</span>
Status Values:
  • activo: Active patient (green)
  • pendiente: Pending patient (yellow)
  • urgente: Urgent patient (red)

Search Functionality

The search feature provides real-time filtering:
1

User Types

User enters search text in the input field
2

Two-way Binding

[(ngModel)] updates searchText property automatically
3

Getter Recomputes

filteredPatients getter is called on each change detection cycle
4

Table Updates

Template rerenders with filtered results
The component embeds the NewAppointment component in a modal:

Event Handling

(close)
EventEmitter<void>
Emitted when the user closes the appointment modal
(created)
EventEmitter<void>
Emitted when a new appointment is successfully created
Both events trigger closeNewAppointmentModal() to dismiss the modal.

Click-Outside Detection

<div class="modal-overlay" (click)="closeNewAppointmentModal()">
  <div class="modal-content" (click)="$event.stopPropagation()">
    <!-- modal content -->
  </div>
</div>
Clicking the overlay closes the modal, while clicking inside the modal prevents propagation.

Router Navigation

The component uses RouterModule for navigation:

View Patient Details

<a [routerLink]="['/patient', patient.id]" class="view-details">
  Ver detalles
</a>
Navigates to /patient/{id} to view detailed patient information.

New Patient

<button class="btn-new" routerLink="/patient/new">
  Nuevo Paciente
</button>
Navigates to /patient/new to create a new patient record.

Customization

Add Additional Search Fields

Extend the filtering logic to include more fields:
get filteredPatients() {
  if (!this.searchText) {
    return this.patients;
  }
  const search = this.searchText.toLowerCase();
  return this.patients.filter(p =>
    (p.nombre?.toLowerCase().includes(search) || false) ||
    (p.email?.toLowerCase().includes(search) || false) ||
    (p.phone?.includes(search) || false) ||
    (p.address?.toLowerCase().includes(search) || false)
  );
}

Add Sorting

Implement column sorting:
export class Patient implements OnInit {
  sortColumn: string = '';
  sortDirection: 'asc' | 'desc' = 'asc';

  sortBy(column: string): void {
    if (this.sortColumn === column) {
      this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc';
    } else {
      this.sortColumn = column;
      this.sortDirection = 'asc';
    }
  }

  get filteredPatients() {
    let filtered = /* existing filter logic */;
    
    if (this.sortColumn) {
      filtered = filtered.sort((a, b) => {
        const aVal = a[this.sortColumn];
        const bVal = b[this.sortColumn];
        const modifier = this.sortDirection === 'asc' ? 1 : -1;
        return aVal > bVal ? modifier : -modifier;
      });
    }
    
    return filtered;
  }
}

Add Pagination

Implement pagination for large patient lists:
export class Patient implements OnInit {
  currentPage: number = 1;
  pageSize: number = 10;

  get paginatedPatients() {
    const filtered = this.filteredPatients;
    const start = (this.currentPage - 1) * this.pageSize;
    return filtered.slice(start, start + this.pageSize);
  }

  get totalPages() {
    return Math.ceil(this.filteredPatients.length / this.pageSize);
  }
}

Patient Service

Service for patient data management

Patient Management

Patient management feature documentation

Appointment View

Appointment listing component

Menu

Navigation menu component

Build docs developers (and LLMs) love