From 185f616cad86eeba1ac5065c4b637f964a7cf9ec Mon Sep 17 00:00:00 2001 From: Alfred Ryvarden Date: Thu, 25 Sep 2025 22:12:57 +0200 Subject: [PATCH] done with base and all extensions --- angular.json | 3 ++ src/app/app-routing.module.ts | 8 +++- src/app/contacts/add/add.component.css | 25 ++++++++++ src/app/contacts/add/add.component.html | 19 ++++++++ src/app/contacts/add/add.component.ts | 35 ++++++++++++++ src/app/contacts/contacts-routing.module.ts | 19 ++++++++ src/app/contacts/contacts.module.ts | 24 ++++++++++ src/app/contacts/contacts.service.ts | 46 ++++++++++++++++++ src/app/contacts/data/contacts.ts | 11 +++++ src/app/contacts/edit/edit.component.css | 25 ++++++++++ src/app/contacts/edit/edit.component.html | 25 ++++++++++ src/app/contacts/edit/edit.component.ts | 52 +++++++++++++++++++++ src/app/contacts/list/list.component.css | 11 +++++ src/app/contacts/list/list.component.html | 10 ++++ src/app/contacts/list/list.component.ts | 18 +++++++ src/app/contacts/models/contact.ts | 7 +++ src/app/contacts/view/view.component.css | 0 src/app/contacts/view/view.component.html | 8 ++++ src/app/contacts/view/view.component.ts | 23 +++++++++ src/app/layout/menu/menu.component.html | 4 +- 20 files changed, 370 insertions(+), 3 deletions(-) create mode 100644 src/app/contacts/add/add.component.css create mode 100644 src/app/contacts/add/add.component.html create mode 100644 src/app/contacts/add/add.component.ts create mode 100644 src/app/contacts/contacts-routing.module.ts create mode 100644 src/app/contacts/contacts.module.ts create mode 100644 src/app/contacts/contacts.service.ts create mode 100644 src/app/contacts/data/contacts.ts create mode 100644 src/app/contacts/edit/edit.component.css create mode 100644 src/app/contacts/edit/edit.component.html create mode 100644 src/app/contacts/edit/edit.component.ts create mode 100644 src/app/contacts/list/list.component.css create mode 100644 src/app/contacts/list/list.component.html create mode 100644 src/app/contacts/list/list.component.ts create mode 100644 src/app/contacts/models/contact.ts create mode 100644 src/app/contacts/view/view.component.css create mode 100644 src/app/contacts/view/view.component.html create mode 100644 src/app/contacts/view/view.component.ts diff --git a/angular.json b/angular.json index 7a4f1ef7..be4c78d3 100644 --- a/angular.json +++ b/angular.json @@ -94,5 +94,8 @@ } } } + }, + "cli": { + "analytics": false } } diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 02972627..d9a92e00 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -1,7 +1,13 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -const routes: Routes = []; +const routes: Routes = [ + { path: '', redirectTo: '/contacts', pathMatch: 'full' }, + { + path: 'contacts', + loadChildren: () => import('./contacts/contacts.module').then(m => m.ContactsModule) + } +]; @NgModule({ imports: [RouterModule.forRoot(routes)], diff --git a/src/app/contacts/add/add.component.css b/src/app/contacts/add/add.component.css new file mode 100644 index 00000000..380085ff --- /dev/null +++ b/src/app/contacts/add/add.component.css @@ -0,0 +1,25 @@ +:host { + display: flex; + box-sizing: border-box; + width: 40vw; + flex-direction: column; +} + +form { + display: flex; + flex-direction: column; + flex: 1; + gap: 0.5em; +} + +label { + font-weight: bold; +} + +.actions { + display: flex; +} + +.spacer { + flex: 1; +} \ No newline at end of file diff --git a/src/app/contacts/add/add.component.html b/src/app/contacts/add/add.component.html new file mode 100644 index 00000000..8dbe55c4 --- /dev/null +++ b/src/app/contacts/add/add.component.html @@ -0,0 +1,19 @@ +

Create Contact

+
+ + + + + + + + + + + + +
+
+ +
+
\ No newline at end of file diff --git a/src/app/contacts/add/add.component.ts b/src/app/contacts/add/add.component.ts new file mode 100644 index 00000000..eb95841a --- /dev/null +++ b/src/app/contacts/add/add.component.ts @@ -0,0 +1,35 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { Router } from '@angular/router'; +import { ContactsService } from '../contacts.service'; + +@Component({ + selector: 'app-add', + templateUrl: './add.component.html', + styleUrls: ['./add.component.css'] +}) +export class AddComponent implements OnInit { + contactForm: FormGroup; + + constructor( + private fb: FormBuilder, + private contactsService: ContactsService, + private router: Router + ) { + this.contactForm = this.fb.group({ + firstName: ['', Validators.required], + lastName: ['', Validators.required], + street: ['', Validators.required], + city: ['', Validators.required] + }); + } + + ngOnInit(): void {} + + onSubmit(): void { + if (this.contactForm.valid) { + this.contactsService.addContact(this.contactForm.value); + this.router.navigate(['/contacts']); + } + } +} \ No newline at end of file diff --git a/src/app/contacts/contacts-routing.module.ts b/src/app/contacts/contacts-routing.module.ts new file mode 100644 index 00000000..090a1901 --- /dev/null +++ b/src/app/contacts/contacts-routing.module.ts @@ -0,0 +1,19 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { ListComponent } from './list/list.component'; +import { AddComponent } from './add/add.component'; +import { ViewComponent } from './view/view.component'; +import { EditComponent } from './edit/edit.component'; + +const routes: Routes = [ + { path: '', component: ListComponent }, + { path: 'add', component: AddComponent }, + { path: ':id', component: ViewComponent }, + { path: ':id/edit', component: EditComponent } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class ContactsRoutingModule { } \ No newline at end of file diff --git a/src/app/contacts/contacts.module.ts b/src/app/contacts/contacts.module.ts new file mode 100644 index 00000000..cab160b8 --- /dev/null +++ b/src/app/contacts/contacts.module.ts @@ -0,0 +1,24 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ReactiveFormsModule } from '@angular/forms'; + +import { ContactsRoutingModule } from './contacts-routing.module'; +import { ListComponent } from './list/list.component'; +import { AddComponent } from './add/add.component'; +import { ViewComponent } from './view/view.component'; +import { EditComponent } from './edit/edit.component'; + +@NgModule({ + declarations: [ + ListComponent, + AddComponent, + ViewComponent, + EditComponent + ], + imports: [ + CommonModule, + ReactiveFormsModule, + ContactsRoutingModule + ] +}) +export class ContactsModule { } \ No newline at end of file diff --git a/src/app/contacts/contacts.service.ts b/src/app/contacts/contacts.service.ts new file mode 100644 index 00000000..75dfac29 --- /dev/null +++ b/src/app/contacts/contacts.service.ts @@ -0,0 +1,46 @@ +import { Injectable } from '@angular/core'; +import { Contact } from './models/contact'; +import { contacts } from './data/contacts'; + +@Injectable({ + providedIn: 'root' +}) +export class ContactsService { + private contacts: Contact[] = [...contacts]; + private nextId = 2; + + getContacts(): Contact[] { + return this.contacts; + } + + getContactById(id: number): Contact | undefined { + return this.contacts.find(contact => contact.id === id); + } + + addContact(contact: Omit): Contact { + const newContact: Contact = { + ...contact, + id: this.nextId++ + }; + this.contacts.push(newContact); + return newContact; + } + + updateContact(id: number, contact: Omit): Contact | null { + const index = this.contacts.findIndex(c => c.id === id); + if (index !== -1) { + this.contacts[index] = { ...contact, id }; + return this.contacts[index]; + } + return null; + } + + deleteContact(id: number): boolean { + const index = this.contacts.findIndex(c => c.id === id); + if (index !== -1) { + this.contacts.splice(index, 1); + return true; + } + return false; + } +} \ No newline at end of file diff --git a/src/app/contacts/data/contacts.ts b/src/app/contacts/data/contacts.ts new file mode 100644 index 00000000..3d9eac92 --- /dev/null +++ b/src/app/contacts/data/contacts.ts @@ -0,0 +1,11 @@ +import { Contact } from '../models/contact'; + +export const contacts: Contact[] = [ + { + id: 1, + firstName: 'Joe', + lastName: 'Blogs', + street: '123 Main St', + city: 'London' + } +]; \ No newline at end of file diff --git a/src/app/contacts/edit/edit.component.css b/src/app/contacts/edit/edit.component.css new file mode 100644 index 00000000..380085ff --- /dev/null +++ b/src/app/contacts/edit/edit.component.css @@ -0,0 +1,25 @@ +:host { + display: flex; + box-sizing: border-box; + width: 40vw; + flex-direction: column; +} + +form { + display: flex; + flex-direction: column; + flex: 1; + gap: 0.5em; +} + +label { + font-weight: bold; +} + +.actions { + display: flex; +} + +.spacer { + flex: 1; +} \ No newline at end of file diff --git a/src/app/contacts/edit/edit.component.html b/src/app/contacts/edit/edit.component.html new file mode 100644 index 00000000..e3fa1d57 --- /dev/null +++ b/src/app/contacts/edit/edit.component.html @@ -0,0 +1,25 @@ +
+

Edit Contact

+
+ + + + + + + + + + + + +
+
+ +
+
+
+ +

Contact not found

+

The contact you are trying to edit does not exist.

+
\ No newline at end of file diff --git a/src/app/contacts/edit/edit.component.ts b/src/app/contacts/edit/edit.component.ts new file mode 100644 index 00000000..1ea1d8d0 --- /dev/null +++ b/src/app/contacts/edit/edit.component.ts @@ -0,0 +1,52 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; +import { Contact } from '../models/contact'; +import { ContactsService } from '../contacts.service'; + +@Component({ + selector: 'app-edit', + templateUrl: './edit.component.html', + styleUrls: ['./edit.component.css'] +}) +export class EditComponent implements OnInit { + contactForm: FormGroup; + contact: Contact | undefined; + contactId: number; + + constructor( + private fb: FormBuilder, + private route: ActivatedRoute, + private router: Router, + private contactsService: ContactsService + ) { + this.contactForm = this.fb.group({ + firstName: ['', Validators.required], + lastName: ['', Validators.required], + street: ['', Validators.required], + city: ['', Validators.required] + }); + this.contactId = 0; + } + + ngOnInit(): void { + this.contactId = Number(this.route.snapshot.paramMap.get('id')); + this.contact = this.contactsService.getContactById(this.contactId); + + if (this.contact) { + this.contactForm.patchValue({ + firstName: this.contact.firstName, + lastName: this.contact.lastName, + street: this.contact.street, + city: this.contact.city + }); + } + } + + onSubmit(): void { + if (this.contactForm.valid && this.contact) { + this.contactsService.updateContact(this.contactId, this.contactForm.value); + this.router.navigate(['/contacts', this.contactId]); + } + } +} \ No newline at end of file diff --git a/src/app/contacts/list/list.component.css b/src/app/contacts/list/list.component.css new file mode 100644 index 00000000..01f87f24 --- /dev/null +++ b/src/app/contacts/list/list.component.css @@ -0,0 +1,11 @@ +:host { + padding: 0.5em; + width: 400px; +} + +.contact { + display: flex; + justify-content: space-between; + border-bottom: 1px solid #ddd; + padding: 0.5em 0; +} \ No newline at end of file diff --git a/src/app/contacts/list/list.component.html b/src/app/contacts/list/list.component.html new file mode 100644 index 00000000..e4764ccd --- /dev/null +++ b/src/app/contacts/list/list.component.html @@ -0,0 +1,10 @@ +

Contacts

+
+
+ {{ contact.firstName }} {{ contact.lastName }} + View +
+
+ +
No contacts yet
+
\ No newline at end of file diff --git a/src/app/contacts/list/list.component.ts b/src/app/contacts/list/list.component.ts new file mode 100644 index 00000000..ccfb0ee2 --- /dev/null +++ b/src/app/contacts/list/list.component.ts @@ -0,0 +1,18 @@ +import { Component, OnInit } from '@angular/core'; +import { Contact } from '../models/contact'; +import { ContactsService } from '../contacts.service'; + +@Component({ + selector: 'app-list', + templateUrl: './list.component.html', + styleUrls: ['./list.component.css'] +}) +export class ListComponent implements OnInit { + contacts: Contact[] = []; + + constructor(private contactsService: ContactsService) { } + + ngOnInit(): void { + this.contacts = this.contactsService.getContacts(); + } +} \ No newline at end of file diff --git a/src/app/contacts/models/contact.ts b/src/app/contacts/models/contact.ts new file mode 100644 index 00000000..eecadba4 --- /dev/null +++ b/src/app/contacts/models/contact.ts @@ -0,0 +1,7 @@ +export interface Contact { + id: number; + firstName: string; + lastName: string; + street: string; + city: string; +} \ No newline at end of file diff --git a/src/app/contacts/view/view.component.css b/src/app/contacts/view/view.component.css new file mode 100644 index 00000000..e69de29b diff --git a/src/app/contacts/view/view.component.html b/src/app/contacts/view/view.component.html new file mode 100644 index 00000000..2fee28a4 --- /dev/null +++ b/src/app/contacts/view/view.component.html @@ -0,0 +1,8 @@ + +

{{ contact.firstName }} {{ contact.lastName }}

+

{{ contact.street }}, {{ contact.city }}

+ Edit +
+ +

Contact not found

+
\ No newline at end of file diff --git a/src/app/contacts/view/view.component.ts b/src/app/contacts/view/view.component.ts new file mode 100644 index 00000000..1341e30f --- /dev/null +++ b/src/app/contacts/view/view.component.ts @@ -0,0 +1,23 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Contact } from '../models/contact'; +import { ContactsService } from '../contacts.service'; + +@Component({ + selector: 'app-view', + templateUrl: './view.component.html', + styleUrls: ['./view.component.css'] +}) +export class ViewComponent implements OnInit { + contact: Contact | undefined; + + constructor( + private route: ActivatedRoute, + private contactsService: ContactsService + ) { } + + ngOnInit(): void { + const id = Number(this.route.snapshot.paramMap.get('id')); + this.contact = this.contactsService.getContactById(id); + } +} \ No newline at end of file diff --git a/src/app/layout/menu/menu.component.html b/src/app/layout/menu/menu.component.html index 7c5ec7a2..aed3259d 100644 --- a/src/app/layout/menu/menu.component.html +++ b/src/app/layout/menu/menu.component.html @@ -1,5 +1,5 @@

Menu