Complete SMS Application in Angular
Level: Advanced
ℹ️ What You'll Learn
- Full SMS application architecture
- Feature modules organization
- Service layer design
- Complete routing setup
- Forms and validation
- HTTP integration
- Error handling
Project Structure
sms-portal/
├── src/
│ ├── app/
│ │ ├── core/
│ │ │ ├── auth.service.ts
│ │ │ ├── auth.guard.ts
│ │ │ └── http.interceptor.ts
│ │ ├── shared/
│ │ │ ├── navbar/
│ │ │ ├── footer/
│ │ │ └── pipes/
│ │ ├── features/
│ │ │ ├── students/
│ │ │ │ ├── components/
│ │ │ │ │ ├── student-list/
│ │ │ │ │ ├── student-detail/
│ │ │ │ │ └── student-form/
│ │ │ │ ├── services/
│ │ │ │ │ └── student.service.ts
│ │ │ │ └── student.routes.ts
│ │ │ ├── exams/
│ │ │ └── fees/
│ │ └── app.routes.ts
│ └── main.ts
App Routes
// app.routes.ts
export const routes: Routes = [
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
{
path: 'login',
component: LoginComponent
},
{
path: 'dashboard',
component: DashboardComponent,
canActivate: [authGuard]
},
{
path: 'students',
canActivate: [authGuard],
loadChildren: () => import('./features/students/student.routes')
.then(m => m.STUDENT_ROUTES)
},
{
path: 'exams',
canActivate: [authGuard],
loadChildren: () => import('./features/exams/exam.routes')
.then(m => m.EXAM_ROUTES)
},
{
path: '**',
redirectTo: '/dashboard'
}
];
Student Feature Module
// features/students/student.routes.ts
export const STUDENT_ROUTES: Routes = [
{ path: '', component: StudentListComponent },
{ path: 'new', component: StudentFormComponent },
{ path: ':id', component: StudentDetailComponent },
{ path: ':id/edit', component: StudentFormComponent }
];
// features/students/services/student.service.ts
@Injectable({ providedIn: 'root' })
export class StudentService {
private apiUrl = `${environment.apiUrl}/students`;
constructor(private http: HttpClient) {}
getStudents(filters?: any) {
return this.http.get<Student[]>(this.apiUrl, { params: filters });
}
getStudent(id: number) {
return this.http.get<Student>(`${this.apiUrl}/${id}`);
}
createStudent(student: Omit<Student, 'id'>) {
return this.http.post<Student>(this.apiUrl, student);
}
updateStudent(id: number, student: Student) {
return this.http.put<Student>(`${this.apiUrl}/${id}`, student);
}
deleteStudent(id: number) {
return this.http.delete(`${this.apiUrl}/${id}`);
}
}
Student List Component
// features/students/components/student-list/student-list.component.ts
@Component({
selector: 'app-student-list',
templateUrl: './student-list.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class StudentListComponent implements OnInit {
students$: Observable<Student[]>;
loading$ = new BehaviorSubject(false);
error$ = new BehaviorSubject<string | null>(null);
constructor(
private service: StudentService,
private router: Router
) {
this.students$ = this.service.getStudents();
}
ngOnInit() {
this.loadStudents();
}
loadStudents() {
this.loading$.next(true);
this.service.getStudents()
.pipe(
finalize(() => this.loading$.next(false)),
catchError(error => {
this.error$.next('Failed to load students');
return of([]);
})
)
.subscribe();
}
deleteStudent(id: number) {
if (confirm('Delete student?')) {
this.service.deleteStudent(id)
.pipe(
tap(() => this.loadStudents())
)
.subscribe();
}
}
editStudent(id: number) {
this.router.navigate(['/students', id, 'edit']);
}
trackByStudentId(index: number, student: Student) {
return student.id;
}
}
<!-- student-list.component.html -->
<div class="container">
<div class="header">
<h1>Students</h1>
<button (click)="router.navigate(['/students/new'])">Add Student</button>
</div>
<div *ngIf="loading$ | async" class="loading">Loading...</div>
<div *ngIf="error$ | async as error" class="alert-error">
{{ error }}
</div>
<table *ngIf="students$ | async as students">
<thead>
<tr>
<th>Roll #</th>
<th>Name</th>
<th>Email</th>
<th>Class</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let student of students; trackBy: trackByStudentId">
<td>{{ student.rollNumber }}</td>
<td>{{ student.name }}</td>
<td>{{ student.email }}</td>
<td>{{ student.className }}</td>
<td>
<span [ngClass]="'status-' + (student.status | lowercase)">
{{ student.status }}
</span>
</td>
<td>
<button (click)="editStudent(student.id)">Edit</button>
<button (click)="deleteStudent(student.id)">Delete</button>
</td>
</tr>
</tbody>
</table>
</div>
Student Form Component
export class StudentFormComponent implements OnInit, OnDestroy {
form: FormGroup;
loading = false;
error: string | null = null;
private destroy$ = new Subject<void>();
constructor(
private fb: FormBuilder,
private service: StudentService,
private route: ActivatedRoute,
private router: Router
) {
this.form = this.fb.group({
rollNumber: ['', [Validators.required, Validators.pattern(/^[0-9]+$/)]],
name: ['', [Validators.required, Validators.minLength(3)]],
email: ['', [Validators.required, Validators.email]],
className: ['10', Validators.required],
status: ['Active', Validators.required]
});
}
ngOnInit() {
// Check if editing
this.route.params
.pipe(
switchMap(params => {
if (params['id']) {
return this.service.getStudent(params['id']);
}
return of(null);
}),
takeUntil(this.destroy$)
)
.subscribe(student => {
if (student) {
this.form.patchValue(student);
}
});
}
onSubmit() {
if (this.form.invalid) return;
this.loading = true;
const id = this.route.snapshot.paramMap.get('id');
const request = id
? this.service.updateStudent(+id, this.form.value)
: this.service.createStudent(this.form.value);
request
.pipe(
tap(() => {
this.router.navigate(['/students']);
}),
catchError(error => {
this.error = error.message;
this.loading = false;
return of(null);
}),
takeUntil(this.destroy$)
)
.subscribe();
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}
Key Patterns Used
- Feature modules (lazy loaded)
- Service layer for API
- Routing with guards
- Reactive forms
- Observables and pipes
- Error handling
- Loading states
- Change detection optimization
Architecture Benefits
✓ Modular structure ✓ Separation of concerns ✓ Easy to test ✓ Scalable ✓ Maintainable ✓ Reusable services
SMS App Features
- Student management (CRUD)
- Form validation
- Error handling
- Loading states
- Role-based access (guards)
- API integration
- List pagination
- Search/filter
Key Takeaways
- Feature modules = separate concerns
- Services = reusable logic
- Guards = route protection
- Observables = async operations
- Forms = user input
- Error handling = user feedback
- Testing = confidence in code
💡 Backend Developer Tip
This structure mirrors ASP.NET Core architecture: Core = infrastructure, Shared = common utilities, Features = business logic. Same principles, Angular implementation.
⚠️ Production Checklist
- Authentication working
- Error handling complete
- Loading states implemented
- Form validation working
- Tests passing
- Build optimized
- Environment configs correct
- CORS configured
- API documentation done
- Monitoring setup
🤖Use AI to Learn Faster
Use ChatGPT, Claude, or Copilot to go deeper on Complete Angular SMS. Try these prompts:
"How do feature modules improve scalability?""Why separate services from components?""What's complete SMS app workflow?""Quiz me on Angular architecture"
💡 Tip: After reading this article, paste your own code into AI and ask "What could go wrong here and why?" — fastest way to find edge cases and deepen understanding.
nexcoding.in
Have questions on your tech stack, ongoing projects, or need one-to-one training?