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..c1db6726 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -1,7 +1,17 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
+import { ListComponent } from './contacts/list/list.component';
+import { AddComponent } from './contacts/add/add.component';
+import { ViewComponent } from './contacts/view/view.component';
+import { EditComponent } from './contacts/edit/edit.component';
-const routes: Routes = [];
+
+const routes: Routes = [
+ { path: 'contacts', component: ListComponent },
+ { path: 'contacts/add', component: AddComponent },
+ { path: 'contacts/:id', component: ViewComponent },
+ { path: 'contacts/edit/:id', component: EditComponent },
+];
@NgModule({
imports: [RouterModule.forRoot(routes)],
diff --git a/src/app/app.component.css b/src/app/app.component.css
index 31d9e249..e69de29b 100644
--- a/src/app/app.component.css
+++ b/src/app/app.component.css
@@ -1,9 +0,0 @@
-:host {
- display: flex;
-}
-
-.page {
- flex: 1;
- display: flex;
- justify-content: center;
-}
diff --git a/src/app/app.component.html b/src/app/app.component.html
index 17aaa0c6..cd9aeee2 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -1,4 +1,14 @@
-
-
+
+
+
Contact Application
+
This really has pet information
+
+
+
diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts
new file mode 100644
index 00000000..ee5230ea
--- /dev/null
+++ b/src/app/app.component.spec.ts
@@ -0,0 +1,35 @@
+import { TestBed } from '@angular/core/testing';
+import { RouterModule } from '@angular/router';
+import { AppComponent } from './app.component';
+
+describe('AppComponent', () => {
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [
+ RouterModule.forRoot([])
+ ],
+ declarations: [
+ AppComponent
+ ],
+ }).compileComponents();
+ });
+
+ it('should create the app', () => {
+ const fixture = TestBed.createComponent(AppComponent);
+ const app = fixture.componentInstance;
+ expect(app).toBeTruthy();
+ });
+
+ it(`should have as title 'angular-pet-workshop-0'`, () => {
+ const fixture = TestBed.createComponent(AppComponent);
+ const app = fixture.componentInstance;
+ expect(app.title).toEqual('angular-pet-workshop-0');
+ });
+
+ it('should render title', () => {
+ const fixture = TestBed.createComponent(AppComponent);
+ fixture.detectChanges();
+ const compiled = fixture.nativeElement as HTMLElement;
+ expect(compiled.querySelector('h1')?.textContent).toContain('Hello, angular-adress-book');
+ });
+});
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index b82791ab..62948c32 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -3,8 +3,8 @@ import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
- styleUrls: ['./app.component.css']
+ styleUrl: './app.component.css'
})
export class AppComponent {
- title = 'angular-address-book';
+ title = 'angular-pet-workshop-0';
}
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 8207184c..9d76b3d1 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -4,10 +4,11 @@ import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { LayoutModule } from './layout/layout.module';
+import { HttpClientModule } from '@angular/common/http';
@NgModule({
declarations: [AppComponent],
- imports: [BrowserModule, AppRoutingModule, LayoutModule],
+ imports: [BrowserModule, AppRoutingModule, LayoutModule, HttpClientModule],
bootstrap: [AppComponent],
})
-export class AppModule {}
+export class AppModule {}
\ No newline at end of file
diff --git a/src/app/contacts/add/add.component.css b/src/app/contacts/add/add.component.css
new file mode 100644
index 00000000..e69de29b
diff --git a/src/app/contacts/add/add.component.html b/src/app/contacts/add/add.component.html
new file mode 100644
index 00000000..1d1061d1
--- /dev/null
+++ b/src/app/contacts/add/add.component.html
@@ -0,0 +1,78 @@
+
+
Add 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..a66ccef3
--- /dev/null
+++ b/src/app/contacts/add/add.component.ts
@@ -0,0 +1,58 @@
+import { Component, inject } from '@angular/core';
+import {FormBuilder, FormGroup, ReactiveFormsModule, Validators} from "@angular/forms";
+import {ContactsService} from "../contacts.service";
+import {Router} from "@angular/router";
+import { Contact } from '../models/contact';
+
+@Component({
+ selector: 'app-add',
+ standalone: true,
+ imports: [
+ ReactiveFormsModule
+ ],
+ templateUrl: './add.component.html',
+ styleUrl: './add.component.css'
+})
+export class AddComponent {
+ private router = inject(Router);
+ private readonly contactService: ContactsService;
+ public contactForm: FormGroup;
+
+ constructor(
+ private formBuilder: FormBuilder,
+ private readonly service: ContactsService
+ ) {
+ this.contactForm = this.formBuilder.group({
+ firstName: ['', Validators.required],
+ lastName: ['', Validators.required],
+ street: ['', Validators.required],
+ city: ['', Validators.required],
+ });
+ this.contactService = service;
+ }
+ cancel(): void {
+ this.router.navigate(['/contacts']);
+ }
+ addContact(): void {
+ if (this.contactForm.valid) {
+ const newContact: Contact = {
+ firstName: this.contactForm.value.firstName,
+ lastName: this.contactForm.value.lastName,
+ street: this.contactForm.value.street,
+ city: this.contactForm.value.city,
+ };
+
+ this.contactService.addContact(newContact).subscribe({
+ next: (response) => {
+ console.log('Contact added successfully:', response);
+ this.contactForm.reset();
+ this.router.navigate(['/contacts']);
+ },
+ error: (err) => {
+ console.error('Failed to add contact:', err);
+ },
+ });
+ }
+ }
+}
+
diff --git a/src/app/contacts/contacts.module.ts b/src/app/contacts/contacts.module.ts
new file mode 100644
index 00000000..e8be1a69
--- /dev/null
+++ b/src/app/contacts/contacts.module.ts
@@ -0,0 +1,15 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { AddComponent } from './add/add.component';
+import { ViewComponent } from './view/view.component';
+import { ListComponent } from './list/list.component';
+import { EditComponent } from './edit/edit.component';
+import { ReactiveFormsModule } from '@angular/forms';
+import { RouterModule } from '@angular/router';
+
+@NgModule({
+ declarations: [AddComponent, ViewComponent, ListComponent, EditComponent],
+ imports: [CommonModule, ReactiveFormsModule, RouterModule],
+ exports: [AddComponent, ViewComponent, ListComponent],
+})
+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..84ad0a92
--- /dev/null
+++ b/src/app/contacts/contacts.service.ts
@@ -0,0 +1,30 @@
+import { inject, Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Contact } from './models/contact';
+import { CONTACTS } from '../data/contacts';
+import { environment } from '../environment/environment';
+import { Observable, lastValueFrom } from 'rxjs';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class ContactsService {
+ private http = inject(HttpClient);
+
+ public getContactById(id: string): Observable {
+ return this.http.get(`${environment.api}/${id}`);
+ }
+ public getContacts(): Observable {
+ return this.http.get(`${environment.api}`);
+ }
+
+ public addContact(contact: Contact): Observable {
+ return this.http.post(`${environment.api}`, contact);
+ }
+ public updateContact(c: Contact): Observable {
+ return this.http.put(`${environment.api}/${c.id}`, c);
+ }
+ public deleteContactById(id: string): Observable {
+ return this.http.delete(`${environment.api}/${id}`);
+ }
+}
diff --git a/src/app/contacts/edit/edit.component.css b/src/app/contacts/edit/edit.component.css
new file mode 100644
index 00000000..e69de29b
diff --git a/src/app/contacts/edit/edit.component.html b/src/app/contacts/edit/edit.component.html
new file mode 100644
index 00000000..bc2d1952
--- /dev/null
+++ b/src/app/contacts/edit/edit.component.html
@@ -0,0 +1,75 @@
+
+Edit Contact
+
+
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..2ca79e2d
--- /dev/null
+++ b/src/app/contacts/edit/edit.component.ts
@@ -0,0 +1,75 @@
+import { Component, inject, OnInit } from '@angular/core';
+import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
+import { ActivatedRoute, Router } from '@angular/router';
+import { ContactsService } from '../contacts.service';
+import { Observable } from 'rxjs';
+import { Contact } from '../models/contact';
+import { CommonModule } from '@angular/common';
+
+@Component({
+ selector: 'app-edit',
+ standalone: true,
+ imports: [CommonModule, ReactiveFormsModule],
+ templateUrl: './edit.component.html',
+ styleUrl: './edit.component.css',
+})
+export class EditComponent implements OnInit {
+ private readonly router = inject(Router);
+ private readonly route = inject(ActivatedRoute);
+ private readonly formBuilder = inject(FormBuilder);
+ private readonly contactsService = inject(ContactsService);
+
+ public contactForm: FormGroup;
+ public contact$!: Observable;
+ public id: string | null = null;
+
+ constructor() {
+ this.contactForm = this.formBuilder.group({
+ firstName: ['', Validators.required],
+ lastName: ['', Validators.required],
+ street: ['', Validators.required],
+ city: ['', Validators.required],
+ });
+ }
+
+ ngOnInit(): void {
+ this.id = this.route.snapshot.paramMap.get('id');
+ console.log('Editing contact with id:', this.id);
+
+ if (this.id) {
+ this.contact$ = this.contactsService.getContactById(this.id);
+ this.contact$.subscribe((contact) => {
+ if (contact) {
+ this.contactForm.patchValue({
+ firstName: contact.firstName,
+ lastName: contact.lastName,
+ street: contact.street,
+ city: contact.city
+ });
+ console.log('found:', contact);
+ console.log('found$', this.contact$);
+ } else {
+ console.error('Contact not found with id:', this.id);
+ }
+ });
+ }
+ }
+ cancel(): void {
+ this.router.navigate(['/contacts']);
+ }
+ updateContact(): void {
+ if (this.contactForm.valid && this.id) {
+ const updatedContact: Contact = {
+ id: this.id,
+ firstName: this.contactForm.value.firstName,
+ lastName: this.contactForm.value.lastName,
+ street: this.contactForm.value.street,
+ city: this.contactForm.value.city,
+ };
+
+ this.contactsService.updateContact(updatedContact).subscribe(() => {
+ this.router.navigate(['/contacts']);
+ });
+ }
+ }
+}
diff --git a/src/app/contacts/list/list.component.css b/src/app/contacts/list/list.component.css
new file mode 100644
index 00000000..84f87310
--- /dev/null
+++ b/src/app/contacts/list/list.component.css
@@ -0,0 +1,6 @@
+.red {
+ color: red;
+}
+#item1 {
+ color: green;
+}
diff --git a/src/app/contacts/list/list.component.html b/src/app/contacts/list/list.component.html
new file mode 100644
index 00000000..c44f6494
--- /dev/null
+++ b/src/app/contacts/list/list.component.html
@@ -0,0 +1,44 @@
+
+Contacts List
+
+
+
+
+ | Id |
+ First Name |
+ Last Name |
+ Street |
+ City |
+ |
+
+
+
+
+ |
+ {{ c.id }}
+ |
+
+
+ {{ c.firstName }}
+ |
+
+ {{ c.street }} |
+ {{ c.city}} |
+ {{ c.city }} |
+
+ ✂️
+ 🗑️
+ |
+
+
+
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..719cb7c9
--- /dev/null
+++ b/src/app/contacts/list/list.component.ts
@@ -0,0 +1,48 @@
+import { Component, inject } from '@angular/core';
+import { Router, RouterModule } from '@angular/router';
+import { Observable } from 'rxjs/internal/Observable';
+import { Contact } from '../models/contact';
+import { ContactsService } from '../contacts.service';
+import { CommonModule } from '@angular/common';
+
+@Component({
+ selector: 'app-list',
+ standalone: true,
+ imports: [CommonModule, RouterModule],
+ templateUrl: './list.component.html',
+ styleUrl: './list.component.css'
+})
+export class ListComponent {
+ private readonly router = inject(Router);
+
+ public contacts$ = new Observable();
+
+ constructor(private readonly contactsService: ContactsService) {}
+
+ ngOnInit(): void {
+ this.contacts$ = this.contactsService.getContacts();
+ this.contacts$.subscribe((contacts) => {
+ contacts.forEach((contact) => {
+ console.log(contact.firstName + ' ' + contact.lastName);
+ });
+ });
+ }
+ refreshContacts(): void {
+ this.contacts$ = this.contactsService.getContacts();
+ }
+ deleteContact(id: string): void {
+ const confirmed = confirm('Are you sure you want to delete this contact?');
+ if (confirmed) {
+ this.contactsService.deleteContactById(id).subscribe({
+ next: () => {
+ console.log(`Contact with ID ${id} deleted successfully.`);
+ //this.router.navigate(['/contacts']); // this doesn't work as we are already on /contacts so we'll call a refresh method instead
+ this.refreshContacts();
+ },
+ error: (err) => {
+ console.error('Delete failed:', err);
+ },
+ });
+ }
+ }
+}
diff --git a/src/app/contacts/models/contact.ts b/src/app/contacts/models/contact.ts
new file mode 100644
index 00000000..95d0cd5a
--- /dev/null
+++ b/src/app/contacts/models/contact.ts
@@ -0,0 +1,7 @@
+export interface Contact {
+ id?: string | null;
+ firstName: string;
+ lastName: string
+ street: string;
+ city: string;
+}
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..aa9a5ac3
--- /dev/null
+++ b/src/app/contacts/view/view.component.html
@@ -0,0 +1,10 @@
+
+A Contact
+
+
+
+ {{ contact.firstName }}
+
+
Street:{{ contact.street }}
+
{{ contact.city }}
+
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..49d51413
--- /dev/null
+++ b/src/app/contacts/view/view.component.ts
@@ -0,0 +1,22 @@
+import { Component, inject } from '@angular/core';
+import { Observable } from 'rxjs';
+import { Contact } from '../models/contact';
+import { ActivatedRoute } from '@angular/router';
+import { ContactsService } from '../contacts.service';
+import { CommonModule, NgIf } from '@angular/common';
+
+@Component({
+ selector: 'app-view',
+ standalone: true,
+ imports: [CommonModule, NgIf],
+ templateUrl: './view.component.html',
+ styleUrl: './view.component.css'
+})
+export class ViewComponent {
+ contact$ = new Observable();
+ route = inject(ActivatedRoute);
+ id: string | null = this.route.snapshot.paramMap.get('id');
+ constructor(private readonly contactsService: ContactsService) {
+ this.contact$ = this.contactsService.getContactById(this.id as string);
+ }
+}
diff --git a/src/app/data/contacts.json b/src/app/data/contacts.json
new file mode 100644
index 00000000..bca57c14
--- /dev/null
+++ b/src/app/data/contacts.json
@@ -0,0 +1,32 @@
+{
+ "contacts": [
+ {
+ "id": "1",
+ "firstName": "Lola",
+ "lastName": "Pug",
+ "street": "Small Dog Road",
+ "city": "Haribo"
+ },
+ {
+ "id": "2",
+ "firstName": "Red",
+ "lastName": "Cat",
+ "street": "Black and White lane",
+ "city": "Appricot"
+ },
+ {
+ "id": "3",
+ "firstName": "Bella",
+ "lastName": "Cat",
+ "street": "Fluffy black blvd",
+ "city": "Geronimo"
+ },
+ {
+ "id": "6f0e",
+ "firstName": "fa",
+ "lastName": "faa",
+ "street": "faa",
+ "city": "fa"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/app/data/contacts.ts b/src/app/data/contacts.ts
new file mode 100644
index 00000000..e96c46eb
--- /dev/null
+++ b/src/app/data/contacts.ts
@@ -0,0 +1,25 @@
+import { Contact } from '../contacts/models/contact';
+
+export const CONTACTS: Contact[] = [
+ {
+ id: '1',
+ firstName: 'Lola',
+ lastName: 'Pug',
+ street: 'Small Dog Road',
+ city: 'Haribo'
+ },
+ {
+ id: '2',
+ firstName: 'Red',
+ lastName: 'Cat',
+ street: 'Black and White lane',
+ city: 'Appricot'
+ },
+ {
+ id: '3',
+ firstName: 'Bella',
+ lastName: 'Cat',
+ street: 'Fluffy black blvd',
+ city: 'Geronimo'
+ },
+];
\ No newline at end of file
diff --git a/src/app/environment/environment.ts b/src/app/environment/environment.ts
new file mode 100644
index 00000000..1eb966eb
--- /dev/null
+++ b/src/app/environment/environment.ts
@@ -0,0 +1,3 @@
+export const environment = {
+ api: 'http://localhost:3000/contacts',
+};
diff --git a/src/app/layout/menu/menu.component.css b/src/app/layout/menu/menu.component.css
index 75955b3c..e69de29b 100644
--- a/src/app/layout/menu/menu.component.css
+++ b/src/app/layout/menu/menu.component.css
@@ -1,10 +0,0 @@
-:host {
- padding: 0 0.5em;
-}
-
-ul {
- padding: 0;
-}
-li {
- list-style: none;
-}
diff --git a/src/app/layout/menu/menu.component.html b/src/app/layout/menu/menu.component.html
index 7c5ec7a2..923d3401 100644
--- a/src/app/layout/menu/menu.component.html
+++ b/src/app/layout/menu/menu.component.html
@@ -1,5 +1,27 @@
-Menu
-
+
diff --git a/src/app/layout/menu/menu.component.specs.ts b/src/app/layout/menu/menu.component.specs.ts
new file mode 100644
index 00000000..8b5a378e
--- /dev/null
+++ b/src/app/layout/menu/menu.component.specs.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MenuComponent } from './menu.component';
+
+describe('MenuComponent', () => {
+ let component: MenuComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [MenuComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(MenuComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});