From c316750769a7d78705139d0dc395b695f964d2fb Mon Sep 17 00:00:00 2001 From: Vegard Stigen Date: Mon, 8 Sep 2025 13:19:48 +0200 Subject: [PATCH 1/2] done core and extension --- src/app/Data/contacts.ts | 18 ++++++ src/app/app.module.ts | 15 ++++- src/app/contacts/add/add.component.css | 25 +++++++++ src/app/contacts/add/add.component.html | 19 +++++++ src/app/contacts/add/add.component.spec.ts | 23 ++++++++ src/app/contacts/add/add.component.ts | 40 +++++++++++++ src/app/contacts/contacts.module.ts | 16 ++++++ src/app/contacts/contacts.service.spec.ts | 16 ++++++ src/app/contacts/contacts.service.ts | 34 +++++++++++ src/app/contacts/edit/edit.component.css | 25 +++++++++ src/app/contacts/edit/edit.component.html | 27 +++++++++ src/app/contacts/edit/edit.component.spec.ts | 23 ++++++++ src/app/contacts/edit/edit.component.ts | 59 ++++++++++++++++++++ src/app/contacts/list/list.component.css | 11 ++++ src/app/contacts/list/list.component.html | 14 +++++ src/app/contacts/list/list.component.spec.ts | 23 ++++++++ src/app/contacts/list/list.component.ts | 17 ++++++ src/app/contacts/view/view.component.css | 0 src/app/contacts/view/view.component.html | 12 ++++ src/app/contacts/view/view.component.spec.ts | 23 ++++++++ src/app/contacts/view/view.component.ts | 27 +++++++++ src/app/layout/menu/menu.component.html | 4 +- src/app/models/contact.ts | 8 +++ 23 files changed, 476 insertions(+), 3 deletions(-) create mode 100644 src/app/Data/contacts.ts 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.spec.ts create mode 100644 src/app/contacts/add/add.component.ts create mode 100644 src/app/contacts/contacts.module.ts create mode 100644 src/app/contacts/contacts.service.spec.ts create mode 100644 src/app/contacts/contacts.service.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.spec.ts 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.spec.ts create mode 100644 src/app/contacts/list/list.component.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.spec.ts create mode 100644 src/app/contacts/view/view.component.ts create mode 100644 src/app/models/contact.ts diff --git a/src/app/Data/contacts.ts b/src/app/Data/contacts.ts new file mode 100644 index 00000000..fe9d306e --- /dev/null +++ b/src/app/Data/contacts.ts @@ -0,0 +1,18 @@ +import { Contact } from '../models/contact'; + +export const CONTACTS: Contact[] = [ + { + id: 1, + firstName: 'person', + lastName: 'one', + street: '1.st street', + city: 'city', + }, + { + id: 2, + firstName: 'person', + lastName: 'two', + street: '2.nd street', + city: 'town', + }, +]; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 8207184c..5e9424e0 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -4,10 +4,23 @@ import { BrowserModule } from '@angular/platform-browser'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { LayoutModule } from './layout/layout.module'; +import { ContactsModule } from './contacts/contacts.module'; +import { ListComponent } from './contacts/list/list.component'; +import { AddComponent } from './contacts/add/add.component'; +import { EditComponent } from './contacts/edit/edit.component'; +import { ViewComponent } from './contacts/view/view.component'; + +import { RouterModule } from '@angular/router'; +const routes = [ + { path: 'contacts', component: ListComponent }, + { path: 'contacts/new', component: AddComponent }, + { path: 'contacts/view/:id', component: ViewComponent }, + { path: 'contacts/edit/:id', component: EditComponent }, +]; @NgModule({ declarations: [AppComponent], - imports: [BrowserModule, AppRoutingModule, LayoutModule], + imports: [BrowserModule, AppRoutingModule, LayoutModule, ContactsModule, RouterModule.forRoot(routes)], bootstrap: [AppComponent], }) export class AppModule {} diff --git a/src/app/contacts/add/add.component.css b/src/app/contacts/add/add.component.css new file mode 100644 index 00000000..04068da6 --- /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; +} diff --git a/src/app/contacts/add/add.component.html b/src/app/contacts/add/add.component.html new file mode 100644 index 00000000..f06a083e --- /dev/null +++ b/src/app/contacts/add/add.component.html @@ -0,0 +1,19 @@ +

Create Contact

+
+ + + + + + + + + + + + +
+
+ +
+
diff --git a/src/app/contacts/add/add.component.spec.ts b/src/app/contacts/add/add.component.spec.ts new file mode 100644 index 00000000..f4528470 --- /dev/null +++ b/src/app/contacts/add/add.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AddComponent } from './add.component'; + +describe('AddComponent', () => { + let component: AddComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AddComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(AddComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/contacts/add/add.component.ts b/src/app/contacts/add/add.component.ts new file mode 100644 index 00000000..bd199ccd --- /dev/null +++ b/src/app/contacts/add/add.component.ts @@ -0,0 +1,40 @@ +import { Component } from '@angular/core'; +import { Contact } from 'src/app/models/contact'; +import { ContactsService } from '../contacts.service'; +import { FormGroup, FormBuilder, Validators } from '@angular/forms'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'app-add', + templateUrl: './add.component.html', + styleUrl: './add.component.css' +}) +export class AddComponent { + + contactForm: FormGroup; + + constructor( + private readonly formBuilder: FormBuilder, + private readonly contactsService: ContactsService, + private readonly router: Router + ) { + this.contactForm = this.formBuilder.group({ + firstName: ['', Validators.required], + lastName: ['', Validators.required], + street: ['', Validators.required], + city: ['', Validators.required] + }); + } + addContact(): void { + const newContact: Contact = { + id: 0, + firstName: this.contactForm.value.firstName, + lastName: this.contactForm.value.lastName, + street: this.contactForm.value.street, + city: this.contactForm.value.city + }; + this.contactsService.addContact(newContact); + this.contactForm.reset(); + this.router.navigate(['/contacts']); + } +} diff --git a/src/app/contacts/contacts.module.ts b/src/app/contacts/contacts.module.ts new file mode 100644 index 00000000..acfe13ae --- /dev/null +++ b/src/app/contacts/contacts.module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterModule } from '@angular/router'; +import { ViewComponent } from './view/view.component'; +import { ListComponent } from './list/list.component'; +import { EditComponent } from './edit/edit.component'; +import { AddComponent } from './add/add.component'; +import { ReactiveFormsModule } from '@angular/forms'; + + +@NgModule({ + declarations: [ViewComponent, ListComponent, EditComponent, AddComponent], + imports: [CommonModule, RouterModule, ReactiveFormsModule], + exports: [ViewComponent, ListComponent, EditComponent, AddComponent], +}) +export class ContactsModule { } diff --git a/src/app/contacts/contacts.service.spec.ts b/src/app/contacts/contacts.service.spec.ts new file mode 100644 index 00000000..b64b5962 --- /dev/null +++ b/src/app/contacts/contacts.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { ContactsService } from './contacts.service'; + +describe('ContactsService', () => { + let service: ContactsService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(ContactsService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/contacts/contacts.service.ts b/src/app/contacts/contacts.service.ts new file mode 100644 index 00000000..ee201330 --- /dev/null +++ b/src/app/contacts/contacts.service.ts @@ -0,0 +1,34 @@ +import { Injectable } from '@angular/core'; +import { Contact } from '../models/contact'; +import { CONTACTS } from '../Data/contacts'; +import { Observable, of } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class ContactsService { + + public contacts: Contact[] = CONTACTS; + private nextId: number; + + constructor() { + this.nextId = this.contacts.length + 1; + } + + public addContact(contact: Contact): void { + contact.id = this.nextId++; + this.contacts.push(contact); + + } + + getContactById(id: number): Observable { + const contact = this.contacts.find(c => c.id === id); + return of(contact); + } + public editContact(updatedContact: Contact): void { + const index = this.contacts.findIndex(c => c.id === updatedContact.id); + if (index !== -1) { + this.contacts[index] = updatedContact; + } + } +} diff --git a/src/app/contacts/edit/edit.component.css b/src/app/contacts/edit/edit.component.css new file mode 100644 index 00000000..04068da6 --- /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; +} diff --git a/src/app/contacts/edit/edit.component.html b/src/app/contacts/edit/edit.component.html new file mode 100644 index 00000000..9e1968cd --- /dev/null +++ b/src/app/contacts/edit/edit.component.html @@ -0,0 +1,27 @@ +

Edit Contact

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

Contact not found, nothing to edit!!

+
diff --git a/src/app/contacts/edit/edit.component.spec.ts b/src/app/contacts/edit/edit.component.spec.ts new file mode 100644 index 00000000..6676ccfd --- /dev/null +++ b/src/app/contacts/edit/edit.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { EditComponent } from './edit.component'; + +describe('EditComponent', () => { + let component: EditComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [EditComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(EditComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/contacts/edit/edit.component.ts b/src/app/contacts/edit/edit.component.ts new file mode 100644 index 00000000..86aaedf6 --- /dev/null +++ b/src/app/contacts/edit/edit.component.ts @@ -0,0 +1,59 @@ +import { Component, input } from '@angular/core'; +import { Contact } from 'src/app/models/contact'; +import { ContactsService } from '../contacts.service'; +import { FormGroup, FormBuilder, Validators, FormsModule } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; + +@Component({ + selector: 'app-edit', + templateUrl: './edit.component.html', + styleUrl: './edit.component.css' +}) +export class EditComponent { + + contactForm: FormGroup; + contactId: number | null = null; + contact: Contact | null = null; + firstNameValue: string = 'default'; + lastNameValue: string = 'default'; + streetValue: string = 'default'; + cityValue: string = 'default'; + + constructor( + private readonly formBuilder: FormBuilder, + private readonly contactsService: ContactsService, + private readonly router: Router, + private route: ActivatedRoute + ) { + this.contactForm = this.formBuilder.group({ + firstName: ['', Validators.required], + lastName: ['', Validators.required], + street: ['', Validators.required], + city: ['', Validators.required] + }); + } + + editContact(): void { + const updatedContact: Contact = { + id: this.contactId!, + firstName: this.contactForm.value.firstName, + lastName: this.contactForm.value.lastName, + street: this.contactForm.value.street, + city: this.contactForm.value.city + }; + this.contactsService.editContact(updatedContact); + this.contactForm.reset(); + this.router.navigate(['/contacts']); + } + + ngOnInit() { + this.contactId = Number(this.route.snapshot.paramMap.get('id')); + this.contactsService.getContactById(this.contactId).subscribe(contact => { + this.contact = contact!; + }); + this.firstNameValue = this.contact?.firstName ?? 'default'; + this.lastNameValue = this.contact?.lastName ?? 'default'; + this.streetValue = this.contact?.street ?? 'default'; + this.cityValue = this.contact?.city ?? 'default'; + } +} diff --git a/src/app/contacts/list/list.component.css b/src/app/contacts/list/list.component.css new file mode 100644 index 00000000..a7943571 --- /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; +} diff --git a/src/app/contacts/list/list.component.html b/src/app/contacts/list/list.component.html new file mode 100644 index 00000000..03f9f0a1 --- /dev/null +++ b/src/app/contacts/list/list.component.html @@ -0,0 +1,14 @@ +

Contacts

+ + +
+
+ {{contact.firstName}} {{contact.lastName}} + View +
+
+
+ + +
No contacts yet
+
\ No newline at end of file diff --git a/src/app/contacts/list/list.component.spec.ts b/src/app/contacts/list/list.component.spec.ts new file mode 100644 index 00000000..b602c867 --- /dev/null +++ b/src/app/contacts/list/list.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ListComponent } from './list.component'; + +describe('ListComponent', () => { + let component: ListComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ListComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/contacts/list/list.component.ts b/src/app/contacts/list/list.component.ts new file mode 100644 index 00000000..13343447 --- /dev/null +++ b/src/app/contacts/list/list.component.ts @@ -0,0 +1,17 @@ +import { Component } from '@angular/core'; +import { Contact } from 'src/app/models/contact'; +import { ContactsService } from '../contacts.service'; + +@Component({ + selector: 'app-list', + templateUrl: './list.component.html', + styleUrl: './list.component.css' +}) +export class ListComponent { + contacts: Contact[] = []; + + constructor(private readonly contactsService: ContactsService) { + this.contacts = this.contactsService.contacts; + } + +} 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..13bbba72 --- /dev/null +++ b/src/app/contacts/view/view.component.html @@ -0,0 +1,12 @@ + + + +

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

+

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

+ +
+ + +

Contact not found

+
diff --git a/src/app/contacts/view/view.component.spec.ts b/src/app/contacts/view/view.component.spec.ts new file mode 100644 index 00000000..380ab164 --- /dev/null +++ b/src/app/contacts/view/view.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ViewComponent } from './view.component'; + +describe('ViewComponent', () => { + let component: ViewComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ViewComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ViewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/contacts/view/view.component.ts b/src/app/contacts/view/view.component.ts new file mode 100644 index 00000000..865dd090 --- /dev/null +++ b/src/app/contacts/view/view.component.ts @@ -0,0 +1,27 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Contact } from 'src/app/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 | null = null; + contactId: number | null = null; + + constructor( + private route: ActivatedRoute, + private contactsService: ContactsService + ) {} + + ngOnInit() { + this.contactId = Number(this.route.snapshot.paramMap.get('id')); + this.contactsService.getContactById(this.contactId).subscribe(contact => { + this.contact = contact!; + }); + } + +} diff --git a/src/app/layout/menu/menu.component.html b/src/app/layout/menu/menu.component.html index 7c5ec7a2..adbb2ccd 100644 --- a/src/app/layout/menu/menu.component.html +++ b/src/app/layout/menu/menu.component.html @@ -1,5 +1,5 @@

Menu

diff --git a/src/app/models/contact.ts b/src/app/models/contact.ts new file mode 100644 index 00000000..da14bf30 --- /dev/null +++ b/src/app/models/contact.ts @@ -0,0 +1,8 @@ +export interface Contact { + id: number | null; + firstName: string; + lastName: string; + street: string; + city: string; +} + From e4eab13a82f8b1f957a23b0a613ffd4773398b8e Mon Sep 17 00:00:00 2001 From: Vegard Stigen Date: Mon, 8 Sep 2025 13:37:15 +0200 Subject: [PATCH 2/2] fixed edit nevigate back to view --- src/app/contacts/edit/edit.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/contacts/edit/edit.component.ts b/src/app/contacts/edit/edit.component.ts index 86aaedf6..dca699c4 100644 --- a/src/app/contacts/edit/edit.component.ts +++ b/src/app/contacts/edit/edit.component.ts @@ -43,7 +43,7 @@ export class EditComponent { }; this.contactsService.editContact(updatedContact); this.contactForm.reset(); - this.router.navigate(['/contacts']); + this.router.navigate([`/contacts/view/${this.contactId}`]); } ngOnInit() {