From dc391c644b77b9ec8d68951f0078cc899597127e Mon Sep 17 00:00:00 2001
From: OZGCloud <ozgcloud@mgm-tp.com>
Date: Tue, 29 Oct 2024 12:07:47 +0100
Subject: [PATCH] OZG-6720 CR Kommentare

---
 ...sationseinheit-container.component.spec.ts | 17 +++++------
 ...rganisationseinheit-list.component.spec.ts | 16 +++++------
 ...nseinheit-form-container.component.spec.ts | 10 +++----
 .../organisationseinheit-form.component.html  |  2 +-
 ...rganisationseinheit-form.component.spec.ts | 28 ++++++++++---------
 ...isationseinheit-signatur.component.spec.ts | 16 +++++------
 ...organisationseinheit-signatur.component.ts |  2 +-
 .../organisationseinheit.formservice.ts       |  4 ---
 .../organisationseinheit.service.spec.ts      |  4 +--
 .../postfach-form.component.html              |  2 +-
 .../postfach-form/postfach.formservice.ts     |  9 +-----
 .../organisations-einheit.ts                  | 11 +++-----
 .../src/lib/resource/resource.service.ts      | 16 ++++++-----
 .../lib/service/formservice.abstract.spec.ts  | 12 ++++++++
 .../src/lib/service/formservice.abstract.ts   |  4 +++
 15 files changed, 78 insertions(+), 75 deletions(-)

diff --git a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-container.component.spec.ts b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-container.component.spec.ts
index 43ee626c39..7c526c1f8f 100644
--- a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-container.component.spec.ts
+++ b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-container.component.spec.ts
@@ -16,10 +16,14 @@ import { OrganisationsEinheitListComponent } from './organisationseinheit-list/o
 describe('OrganisationsEinheitContainerComponent', () => {
   let component: OrganisationsEinheitContainerComponent;
   let fixture: ComponentFixture<OrganisationsEinheitContainerComponent>;
-
-  const organisationsEinheitService: Mock<OrganisationsEinheitService> = mock(OrganisationsEinheitService);
+  let organisationsEinheitService: Mock<OrganisationsEinheitService>;
 
   beforeEach(async () => {
+    organisationsEinheitService = mock(OrganisationsEinheitService);
+    organisationsEinheitService.getList = jest
+      .fn()
+      .mockReturnValue(of(createStateResource(createAdminOrganisationsEinheitListResource())));
+
     await TestBed.configureTestingModule({
       declarations: [OrganisationsEinheitContainerComponent, MockComponent(OrganisationsEinheitListComponent)],
       imports: [ButtonWithSpinnerComponent],
@@ -28,11 +32,6 @@ describe('OrganisationsEinheitContainerComponent', () => {
 
     fixture = TestBed.createComponent(OrganisationsEinheitContainerComponent);
     component = fixture.componentInstance;
-
-    organisationsEinheitService.getList = jest
-      .fn()
-      .mockReturnValue(of(createStateResource(createAdminOrganisationsEinheitListResource())));
-
     fixture.detectChanges();
   });
 
@@ -83,8 +82,10 @@ describe('OrganisationsEinheitContainerComponent', () => {
 
   describe('template', () => {
     describe('organisationsEinheiten list', () => {
+      const listSelector: string = getDataTestIdOf('organisations-einheit-list');
+
       it('should show list', () => {
-        existsAsHtmlElement(fixture, getDataTestIdOf('organisations-einheit-list'));
+        existsAsHtmlElement(fixture, listSelector);
       });
     });
   });
diff --git a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-list/organisationseinheit-list.component.spec.ts b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-list/organisationseinheit-list.component.spec.ts
index 3129ce926a..0f4f360223 100644
--- a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-list/organisationseinheit-list.component.spec.ts
+++ b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-list/organisationseinheit-list.component.spec.ts
@@ -24,6 +24,8 @@ describe('OrganisationsEinheitListComponent', () => {
   const listSelector: string = getDataTestIdOf('organisations-einheit-list');
   const listItemSelector: string = getDataTestIdOf('organisations-einheit-list-item');
 
+  const organisationsEinheitResource: AdminOrganisationsEinheitResource = createAdminOrganisationsEinheitResource();
+
   beforeEach(async () => {
     await TestBed.configureTestingModule({
       providers: [
@@ -47,9 +49,7 @@ describe('OrganisationsEinheitListComponent', () => {
 
   describe('input', () => {
     describe('organisationsEinheitResources', () => {
-      const organisationsEinheitResource: AdminOrganisationsEinheitResource = createAdminOrganisationsEinheitResource(
-        AdminOrganisationsEinheitSyncResult.NAME_MISMATCH,
-      );
+      organisationsEinheitResource.syncResult = AdminOrganisationsEinheitSyncResult.NAME_MISMATCH;
 
       let listItemElement: HTMLElement;
       let nameElement: Element;
@@ -110,18 +110,16 @@ describe('OrganisationsEinheitListComponent', () => {
     });
     describe('syncResultIsNotOk', () => {
       it('should return true', () => {
-        const organisationsEinheitResource: AdminOrganisationsEinheitResource = createAdminOrganisationsEinheitResource(
-          AdminOrganisationsEinheitSyncResult.NAME_MISMATCH,
-        );
+        organisationsEinheitResource.syncResult = AdminOrganisationsEinheitSyncResult.NAME_MISMATCH;
+
         const result: boolean = component.syncResultIsNotOk(organisationsEinheitResource);
 
         expect(result).toBeTruthy();
       });
 
       it('should return false', () => {
-        const organisationsEinheitResource: AdminOrganisationsEinheitResource = createAdminOrganisationsEinheitResource(
-          AdminOrganisationsEinheitSyncResult.OK,
-        );
+        organisationsEinheitResource.syncResult = AdminOrganisationsEinheitSyncResult.OK;
+
         const result: boolean = component.syncResultIsNotOk(organisationsEinheitResource);
 
         expect(result).toBeFalsy();
diff --git a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-form-container/organisationseinheit-form-container.component.spec.ts b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-form-container/organisationseinheit-form-container.component.spec.ts
index a315bac9c5..fae8d021d6 100644
--- a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-form-container/organisationseinheit-form-container.component.spec.ts
+++ b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-form-container/organisationseinheit-form-container.component.spec.ts
@@ -62,24 +62,24 @@ describe('OrganisationsEinheitFormContainerComponent', () => {
 
   describe('template', () => {
     describe('headline', () => {
-      const headline: string = getDataTestIdOf('organisations-form-container-headline');
+      const headlineSelector: string = getDataTestIdOf('organisations-form-container-headline');
 
       it('should show headline', () => {
-        existsAsHtmlElement(fixture, headline);
+        existsAsHtmlElement(fixture, headlineSelector);
       });
 
       it('should show organisationsEinheit name', () => {
-        const headlineElement: HTMLElement = getElementFromFixture(fixture, headline);
+        const headlineElement: HTMLElement = getElementFromFixture(fixture, headlineSelector);
 
         expect(headlineElement.textContent).toBe(organisationsEinheitResource.name);
       });
     });
 
     describe('organisationsEinheit form', () => {
-      const form: string = getDataTestIdOf('organisations-form');
+      const formSelector: string = getDataTestIdOf('organisations-form');
 
       it('should show form', () => {
-        existsAsHtmlElement(fixture, form);
+        existsAsHtmlElement(fixture, formSelector);
       });
 
       it('should have organisationsEinheitStateResource', () => {
diff --git a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-form-container/organisationseinheit-form/organisationseinheit-form.component.html b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-form-container/organisationseinheit-form/organisationseinheit-form.component.html
index 3e8708c693..3b1b1f0673 100644
--- a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-form-container/organisationseinheit-form/organisationseinheit-form.component.html
+++ b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-form-container/organisationseinheit-form/organisationseinheit-form.component.html
@@ -9,7 +9,7 @@
     ></ods-button-with-spinner>
 
     <span
-      *ngIf="formService.invalidEmpty"
+      *ngIf="formService.isInvalid()"
       data-test-id="invalid-empty-message-span"
       class="m-2 text-red-500"
     >
diff --git a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-form-container/organisationseinheit-form/organisationseinheit-form.component.spec.ts b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-form-container/organisationseinheit-form/organisationseinheit-form.component.spec.ts
index 1976562c9a..9255e2cdbe 100644
--- a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-form-container/organisationseinheit-form/organisationseinheit-form.component.spec.ts
+++ b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-form-container/organisationseinheit-form/organisationseinheit-form.component.spec.ts
@@ -19,10 +19,11 @@ describe('OrganisationsEinheitFormComponent', () => {
   let component: OrganisationsEinheitFormComponent;
   let fixture: ComponentFixture<OrganisationsEinheitFormComponent>;
   let formService: OrganisationsEinheitFormService;
-
-  const organisationsEinheitService: Mock<OrganisationsEinheitService> = mock(OrganisationsEinheitService);
+  let organisationsEinheitService: Mock<OrganisationsEinheitService>;
 
   beforeEach(async () => {
+    organisationsEinheitService = mock(OrganisationsEinheitService);
+
     await TestBed.configureTestingModule({
       declarations: [
         OrganisationsEinheitFormComponent,
@@ -47,15 +48,12 @@ describe('OrganisationsEinheitFormComponent', () => {
   describe('input', () => {
     describe('organisationsEinheitStateResource', () => {
       it('should return resource', () => {
-        const updateOrganisationsEinheitResourceFn: jest.SpyInstance = jest.spyOn(
-          component,
-          'updateOrganisationsEinheitResource',
-        );
-        const organisationsEinheitResource: AdminOrganisationsEinheitResource = createAdminOrganisationsEinheitResource();
+        component.updateOrganisationsEinheitResource = jest.fn();
 
+        const organisationsEinheitResource: AdminOrganisationsEinheitResource = createAdminOrganisationsEinheitResource();
         component.organisationsEinheitStateResource = createStateResource(organisationsEinheitResource);
 
-        expect(updateOrganisationsEinheitResourceFn).toHaveBeenCalledWith(organisationsEinheitResource);
+        expect(component.updateOrganisationsEinheitResource).toHaveBeenCalledWith(organisationsEinheitResource);
       });
     });
   });
@@ -86,19 +84,23 @@ describe('OrganisationsEinheitFormComponent', () => {
 
   describe('template', () => {
     describe('organisationsEinheit signatur component', () => {
+      const signaturComponentSelector: string = getDataTestIdOf('organisations-einheit-signatur-component');
+
       it('should show signatur component', () => {
-        existsAsHtmlElement(fixture, getDataTestIdOf('organisations-einheit-signatur-component'));
+        existsAsHtmlElement(fixture, signaturComponentSelector);
       });
     });
 
     describe('save button', () => {
+      const saveButtonSelector: string = getDataTestIdOf('save-button');
+
       it('should show save button', () => {
-        existsAsHtmlElement(fixture, getDataTestIdOf('save-button'));
+        existsAsHtmlElement(fixture, saveButtonSelector);
       });
     });
 
     describe('invalid message', () => {
-      const invalidMessageSpan: string = getDataTestIdOf('invalid-empty-message-span');
+      const invalidMessageSpanSelector: string = getDataTestIdOf('invalid-empty-message-span');
 
       it('should show if form invalidEmpty', () => {
         const problemDetail: ProblemDetail = {
@@ -109,11 +111,11 @@ describe('OrganisationsEinheitFormComponent', () => {
 
         fixture.detectChanges();
 
-        existsAsHtmlElement(fixture, invalidMessageSpan);
+        existsAsHtmlElement(fixture, invalidMessageSpanSelector);
       });
 
       it('should not show if form valid', () => {
-        notExistsAsHtmlElement(fixture, invalidMessageSpan);
+        notExistsAsHtmlElement(fixture, invalidMessageSpanSelector);
       });
     });
   });
diff --git a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-form-container/organisationseinheit-form/organisationseinheit-signatur/organisationseinheit-signatur.component.spec.ts b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-form-container/organisationseinheit-form/organisationseinheit-signatur/organisationseinheit-signatur.component.spec.ts
index da98a3b876..4d70986f63 100644
--- a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-form-container/organisationseinheit-form/organisationseinheit-signatur/organisationseinheit-signatur.component.spec.ts
+++ b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-form-container/organisationseinheit-form/organisationseinheit-signatur/organisationseinheit-signatur.component.spec.ts
@@ -5,8 +5,8 @@ import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
 import { EffectsModule } from '@ngrx/effects';
 import { StoreModule } from '@ngrx/store';
 import { TextareaEditorComponent } from '@ods/component';
-import { getDataTestIdOf } from 'libs/tech-shared/test/data-test';
 import { MockComponent } from 'ng-mocks';
+import { getDataTestIdOf } from '../../../../../../../../tech-shared/test/data-test';
 import { OrganisationsEinheitService } from '../../../organisationseinheit.service';
 import { OrganisationsEinheitFormService } from '../organisationseinheit.formservice';
 import { OrganisationsEinheitSignaturComponent } from './organisationseinheit-signatur.component';
@@ -14,15 +14,11 @@ import { OrganisationsEinheitSignaturComponent } from './organisationseinheit-si
 describe('OrganisationsEinheitSignaturComponent', () => {
   let component: OrganisationsEinheitSignaturComponent;
   let fixture: ComponentFixture<OrganisationsEinheitSignaturComponent>;
-
-  const formService: OrganisationsEinheitFormService = new OrganisationsEinheitFormService(
-    new FormBuilder(),
-    useFromMock(mock(OrganisationsEinheitService)),
-  );
-
-  const signaturTextarea = getDataTestIdOf('signatur-text');
+  let formService: OrganisationsEinheitFormService;
 
   beforeEach(async () => {
+    formService = new OrganisationsEinheitFormService(new FormBuilder(), useFromMock(mock(OrganisationsEinheitService)));
+
     await TestBed.configureTestingModule({
       imports: [ReactiveFormsModule, NavigationSharedModule, StoreModule.forRoot({}), EffectsModule.forRoot([])],
       declarations: [OrganisationsEinheitSignaturComponent, MockComponent(TextareaEditorComponent)],
@@ -46,8 +42,10 @@ describe('OrganisationsEinheitSignaturComponent', () => {
   describe('template', () => {
     describe('ods-textarea-editor', () => {
       describe('input', () => {
+        const signaturTextareaSelector: string = getDataTestIdOf('signatur-text');
+
         it('should set signatur field', () => {
-          const textAreaEditor = getElementFromFixture(fixture, signaturTextarea);
+          const textAreaEditor: HTMLElement = getElementFromFixture(fixture, signaturTextareaSelector);
 
           expect(textAreaEditor.getAttribute('rows')).toEqual('6');
         });
diff --git a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-form-container/organisationseinheit-form/organisationseinheit-signatur/organisationseinheit-signatur.component.ts b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-form-container/organisationseinheit-form/organisationseinheit-signatur/organisationseinheit-signatur.component.ts
index 112353d7b5..ad670e03d7 100644
--- a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-form-container/organisationseinheit-form/organisationseinheit-signatur/organisationseinheit-signatur.component.ts
+++ b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-form-container/organisationseinheit-form/organisationseinheit-signatur/organisationseinheit-signatur.component.ts
@@ -6,7 +6,7 @@ import { OrganisationsEinheitFormService } from '../organisationseinheit.formser
   templateUrl: './organisationseinheit-signatur.component.html',
 })
 export class OrganisationsEinheitSignaturComponent {
-  protected readonly formServiceClass = OrganisationsEinheitFormService;
+  public readonly formServiceClass = OrganisationsEinheitFormService;
 
   constructor(public formService: OrganisationsEinheitFormService) {}
 }
diff --git a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-form-container/organisationseinheit-form/organisationseinheit.formservice.ts b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-form-container/organisationseinheit-form/organisationseinheit.formservice.ts
index 82beca0dfb..9cb9131e62 100644
--- a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-form-container/organisationseinheit-form/organisationseinheit.formservice.ts
+++ b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-form-container/organisationseinheit-form/organisationseinheit.formservice.ts
@@ -29,8 +29,4 @@ export class OrganisationsEinheitFormService extends AbstractFormService {
   protected getPathPrefix(): string {
     return 'settingBody';
   }
-
-  public get invalidEmpty(): boolean {
-    return this.form.invalid;
-  }
 }
diff --git a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit.service.spec.ts b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit.service.spec.ts
index 77d2d39309..ad3de85839 100644
--- a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit.service.spec.ts
+++ b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit.service.spec.ts
@@ -55,11 +55,11 @@ describe('OrganisationsEinheitService', () => {
     });
 
     it('should call getOrganisationsEinheitUrl', () => {
-      (<any>service).getOrganisationsEinheitUrl = jest.fn();
+      service.getOrganisationsEinheitUrl = jest.fn();
 
       service.onNavigation({ [OrganisationsEinheitService.ORGANISATIONS_EINHEIT_URL]: 'some-uri' });
 
-      expect((<any>service).getOrganisationsEinheitUrl).toHaveBeenCalled();
+      expect(service.getOrganisationsEinheitUrl).toHaveBeenCalled();
     });
   });
 
diff --git a/alfa-client/libs/admin/settings/src/lib/postfach/postfach-container/postfach-form/postfach-form.component.html b/alfa-client/libs/admin/settings/src/lib/postfach/postfach-container/postfach-form/postfach-form.component.html
index a5aace656e..2344782082 100644
--- a/alfa-client/libs/admin/settings/src/lib/postfach/postfach-container/postfach-form/postfach-form.component.html
+++ b/alfa-client/libs/admin/settings/src/lib/postfach/postfach-container/postfach-form/postfach-form.component.html
@@ -45,7 +45,7 @@
   ></ods-button-with-spinner>
 
   <span
-    *ngIf="formService.invalidEmpty"
+    *ngIf="formService.isInvalid()"
     data-test-id="invalid-empty-message-span"
     class="m-2 text-red-500"
   >
diff --git a/alfa-client/libs/admin/settings/src/lib/postfach/postfach-container/postfach-form/postfach.formservice.ts b/alfa-client/libs/admin/settings/src/lib/postfach/postfach-container/postfach-form/postfach.formservice.ts
index ed81ff5fd5..dd9bcc7a37 100644
--- a/alfa-client/libs/admin/settings/src/lib/postfach/postfach-container/postfach-form/postfach.formservice.ts
+++ b/alfa-client/libs/admin/settings/src/lib/postfach/postfach-container/postfach-form/postfach.formservice.ts
@@ -46,17 +46,10 @@ export class PostfachFormService extends AbstractFormService {
   }
 
   private shouldSkipAbsender(postfach: Postfach): boolean {
-    return (
-      isNil(this.source?.absender) &&
-      Object.values(postfach.absender).every((v) => isNil(v) || v.length === 0)
-    );
+    return isNil(this.source?.absender) && Object.values(postfach.absender).every((v) => isNil(v) || v.length === 0);
   }
 
   protected getPathPrefix(): string {
     return 'settingBody';
   }
-
-  public get invalidEmpty(): boolean {
-    return this.form.invalid;
-  }
 }
diff --git a/alfa-client/libs/admin/settings/test/organisations-einheit/organisations-einheit.ts b/alfa-client/libs/admin/settings/test/organisations-einheit/organisations-einheit.ts
index 600e815386..85ef561372 100644
--- a/alfa-client/libs/admin/settings/test/organisations-einheit/organisations-einheit.ts
+++ b/alfa-client/libs/admin/settings/test/organisations-einheit/organisations-einheit.ts
@@ -9,22 +9,19 @@ import {
 } from '../../src';
 import { OrganisationsEinheitListLinkRel } from '../../src/lib/organisationseinheit/organisations-einheit.linkrel';
 
-export function createAdminOrganisationsEinheit(syncResult?: AdminOrganisationsEinheitSyncResult): AdminOrganisationsEinheit {
+export function createAdminOrganisationsEinheit(): AdminOrganisationsEinheit {
   return {
     name: faker.random.word(),
     organisationsEinheitId: faker.random.word(),
-    syncResult: syncResult ?? AdminOrganisationsEinheitSyncResult.OK,
+    syncResult: AdminOrganisationsEinheitSyncResult.OK,
     settings: {
       signatur: faker.random.words(5),
     },
   };
 }
 
-export function createAdminOrganisationsEinheitResource(
-  syncResult?: AdminOrganisationsEinheitSyncResult,
-  linkRel: string[] = [],
-): AdminOrganisationsEinheitResource {
-  return toResource(createAdminOrganisationsEinheit(syncResult), linkRel);
+export function createAdminOrganisationsEinheitResource(linkRel: string[] = []): AdminOrganisationsEinheitResource {
+  return toResource(createAdminOrganisationsEinheit(), linkRel);
 }
 
 export function createAdminOrganisationsEinheitResources(linkRelations: string[] = []): AdminOrganisationsEinheitResource[] {
diff --git a/alfa-client/libs/tech-shared/src/lib/resource/resource.service.ts b/alfa-client/libs/tech-shared/src/lib/resource/resource.service.ts
index 02abe975e8..8b88355c61 100644
--- a/alfa-client/libs/tech-shared/src/lib/resource/resource.service.ts
+++ b/alfa-client/libs/tech-shared/src/lib/resource/resource.service.ts
@@ -120,17 +120,19 @@ export abstract class ResourceService<B extends Resource, T extends Resource> {
   }
 
   public save(toSave: unknown): Observable<StateResource<T>> {
-    const previousResource: T = this.stateResource.value.resource;
-    return this.doSave(previousResource, toSave).pipe(
-      tap((loadedResource: T) => this.stateResource.next(createStateResource(loadedResource))),
-      map(() => this.stateResource.value),
-      catchError((errorResponse: HttpErrorResponse) => this.handleError(errorResponse)),
-    );
+    return this.persist(this.doSave, toSave);
   }
 
   public patch(toPatch: unknown): Observable<StateResource<T>> {
+    return this.persist(this.doPatch, toPatch);
+  }
+
+  private persist(
+    action: (previousResource: T, payload: unknown) => Observable<T>,
+    payload: unknown,
+  ): Observable<StateResource<T>> {
     const previousResource: T = this.stateResource.value.resource;
-    return this.doPatch(previousResource, toPatch).pipe(
+    return action(previousResource, payload).pipe(
       tap((loadedResource: T) => this.stateResource.next(createStateResource(loadedResource))),
       map(() => this.stateResource.value),
       catchError((errorResponse: HttpErrorResponse) => this.handleError(errorResponse)),
diff --git a/alfa-client/libs/tech-shared/src/lib/service/formservice.abstract.spec.ts b/alfa-client/libs/tech-shared/src/lib/service/formservice.abstract.spec.ts
index bff5f0c17f..ea2d29cd9a 100644
--- a/alfa-client/libs/tech-shared/src/lib/service/formservice.abstract.spec.ts
+++ b/alfa-client/libs/tech-shared/src/lib/service/formservice.abstract.spec.ts
@@ -164,6 +164,18 @@ describe('AbstractFormService', () => {
       expect(formService.form.reset).toHaveBeenCalled();
     });
   });
+
+  describe('isInvalid', () => {
+    it('should return false on valid state', () => {
+      expect(formService.isInvalid()).toBeFalsy();
+    });
+
+    it('should return true on invalid state', () => {
+      formService.form.controls[TestFormService.FIELD].setErrors({ required: true });
+
+      expect(formService.isInvalid()).toBeTruthy();
+    });
+  });
 });
 
 class TestFormService extends AbstractFormService {
diff --git a/alfa-client/libs/tech-shared/src/lib/service/formservice.abstract.ts b/alfa-client/libs/tech-shared/src/lib/service/formservice.abstract.ts
index ddbdfbd28d..e14057ca12 100644
--- a/alfa-client/libs/tech-shared/src/lib/service/formservice.abstract.ts
+++ b/alfa-client/libs/tech-shared/src/lib/service/formservice.abstract.ts
@@ -102,4 +102,8 @@ export abstract class AbstractFormService<T extends Resource = Resource> {
   public reset(): void {
     this.form.reset();
   }
+
+  public isInvalid(): boolean {
+    return this.form.invalid;
+  }
 }
-- 
GitLab