-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
44 changed files
with
1,668 additions
and
975 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Forms | ||
|
||
A showcase page that demonstrates how to use the otter forms feature inside an application. | ||
|
||
The page contains both a step by step explanation to guide the users as well as a sample component that can be used as a reference and that illustrates the capabilities of the feature. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { AsyncPipe } from '@angular/common'; | ||
import { AfterViewInit, ChangeDetectionStrategy, Component, QueryList, ViewChildren, ViewEncapsulation } from '@angular/core'; | ||
import { RouterModule } from '@angular/router'; | ||
import { O3rComponent } from '@o3r/core'; | ||
import { CopyTextPresComponent, FormsPresComponent, IN_PAGE_NAV_PRES_DIRECTIVES, InPageNavLink, InPageNavLinkDirective, InPageNavPresService } from '../../components/index'; | ||
|
||
@O3rComponent({ componentType: 'Page' }) | ||
@Component({ | ||
selector: 'o3r-forms', | ||
standalone: true, | ||
imports: [ | ||
RouterModule, | ||
FormsPresComponent, | ||
CopyTextPresComponent, | ||
IN_PAGE_NAV_PRES_DIRECTIVES, | ||
AsyncPipe | ||
], | ||
templateUrl: './forms.template.html', | ||
styleUrl: './forms.style.scss', | ||
encapsulation: ViewEncapsulation.None, | ||
changeDetection: ChangeDetectionStrategy.OnPush | ||
}) | ||
export class FormsComponent implements AfterViewInit { | ||
@ViewChildren(InPageNavLinkDirective) | ||
private readonly inPageNavLinkDirectives!: QueryList<InPageNavLink>; | ||
public links$ = this.inPageNavPresService.links$; | ||
|
||
constructor(private readonly inPageNavPresService: InPageNavPresService) {} | ||
|
||
public ngAfterViewInit() { | ||
this.inPageNavPresService.initialize(this.inPageNavLinkDirectives); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { ComponentFixture, TestBed } from '@angular/core/testing'; | ||
|
||
import { FormsComponent } from './forms.component'; | ||
|
||
describe('FormsComponent', () => { | ||
let component: FormsComponent; | ||
let fixture: ComponentFixture<FormsComponent>; | ||
|
||
beforeEach(async () => { | ||
await TestBed.configureTestingModule({ | ||
imports: [FormsComponent] | ||
}) | ||
.compileComponents(); | ||
|
||
fixture = TestBed.createComponent(FormsComponent); | ||
Check failure on line 15 in apps/showcase/src/app/forms/forms.spec.ts GitHub Actions / checks / test (ubuntu-latest)FormsComponent › should create
Check failure on line 15 in apps/showcase/src/app/forms/forms.spec.ts GitHub Actions / checks / test (windows-latest)FormsComponent › should create
|
||
component = fixture.componentInstance; | ||
fixture.detectChanges(); | ||
}); | ||
|
||
it('should create', () => { | ||
expect(component).toBeTruthy(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
o3r-forms { | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<h1>Forms</h1> | ||
<div class="row"> | ||
<div class="right-nav order-1 order-lg-2 col-12 col-lg-2 sticky-lg-top pt-5 pt-lg-0"> | ||
<o3r-in-page-nav-pres | ||
id="forms-nav" | ||
[links]="links$ | async" | ||
> | ||
</o3r-in-page-nav-pres> | ||
</div> | ||
<div class="order-2 order-lg-1 col-12 col-lg-10"> | ||
<h2 id="forms-description">Description</h2> | ||
<div> | ||
<p>This module provides utilities to enhance the build of Angular reactive forms for specific use cases, including:</p> | ||
<ul> | ||
<li>A container/presenter structure for components</li> | ||
<li>Handling form submission at page (or parent component) level</li> | ||
<li>Displaying the error message outside the form</li> | ||
</ul> | ||
</div> | ||
|
||
<h2 id="forms-example">Example</h2> | ||
<div> | ||
<p> | ||
In the following example, we have a parent component with two subcomponents, each containing a form. | ||
</p> | ||
<p> | ||
The first form requires the user to define their personal information (name and date of birth). | ||
The second form requires the definition of the user's emergency contact information (name, phone number, and email address). | ||
Both forms contain validators, such as certain fields being required or specific values having to follow a certain pattern. | ||
</p> | ||
<p> | ||
The submit of both forms is triggered at parent component level. | ||
</p> | ||
<o3r-forms-pres></o3r-forms-pres> | ||
<p> | ||
Do not hesitate to run the application locally, if not installed yet, follow the <a routerLink="/run-app-locally">instructions</a>. | ||
</p> | ||
<a href="https://github.com/AmadeusITGroup/otter/blob/main/apps/showcase/src/components/showcase/localization" target="_blank" rel="noopener">Source code</a> | ||
</div> | ||
<h2 id="forms-install">How to install</h2> | ||
<o3r-copy-text-pres [wrap]="true" language="bash" text="ng add @o3r/forms"></o3r-copy-text-pres> | ||
<h2 id="forms-references">References</h2> | ||
<div> | ||
<ul> | ||
<li> | ||
<a href="https://github.com/AmadeusITGroup/otter/blob/main/packages/%40o3r/forms/README.md" target="_blank" rel="noopener">Documentation</a> | ||
</li> | ||
</ul> | ||
</div> | ||
</div> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './forms.component'; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# FormsPres | ||
|
||
Showcase of an Otter component with forms |
17 changes: 17 additions & 0 deletions
17
apps/showcase/src/components/showcase/forms/contracts/form-models.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/** Model used to create Personal Info form */ | ||
export interface PersonalInfo { | ||
/** Name */ | ||
name: string; | ||
/** Date of birth */ | ||
dateOfBirth: string; | ||
} | ||
|
||
/** Model used to create Emergency Contact form */ | ||
export interface EmergencyContact { | ||
/** Emergency contact name */ | ||
name: string; | ||
/** Emergency contact phone number */ | ||
phone: string; | ||
/** Emergency contact email address */ | ||
email: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './form-models'; |
138 changes: 138 additions & 0 deletions
138
apps/showcase/src/components/showcase/forms/forms-pres.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
import { AsyncPipe, CommonModule, formatDate } from '@angular/common'; | ||
import { ChangeDetectionStrategy, Component, Input, type OnDestroy, type OnInit, ViewEncapsulation } from '@angular/core'; | ||
import { ReactiveFormsModule, UntypedFormControl } from '@angular/forms'; | ||
import { O3rComponent } from '@o3r/core'; | ||
import { Localization } from '@o3r/localization'; | ||
import { CustomFormValidation } from '@o3r/forms'; | ||
import { Observable, Subject, Subscription } from 'rxjs'; | ||
import { FormsEmergencyContactPresComponent, FormsPersonalInfoPresComponent } from '../../utilities'; | ||
import { EmergencyContact, PersonalInfo } from './contracts'; | ||
import { FormsPresTranslation, translations } from './forms-pres.translation'; | ||
import { dateCustomValidator, formsPresValidatorGlobal } from './forms-pres.validators'; | ||
|
||
@O3rComponent({ componentType: 'Component' }) | ||
@Component({ | ||
selector: 'o3r-forms-pres', | ||
standalone: true, | ||
imports: [ | ||
AsyncPipe, | ||
CommonModule, | ||
FormsEmergencyContactPresComponent, | ||
FormsPersonalInfoPresComponent, | ||
ReactiveFormsModule | ||
], | ||
templateUrl: './forms-pres.template.html', | ||
styleUrl: './forms-pres.style.scss', | ||
encapsulation: ViewEncapsulation.None, | ||
changeDetection: ChangeDetectionStrategy.OnPush | ||
}) | ||
export class FormsPresComponent implements OnInit, OnDestroy { | ||
|
||
/** Localization of the component */ | ||
@Input() | ||
@Localization('./forms-pres.localization.json') | ||
public translations: FormsPresTranslation; | ||
|
||
/** Observable used to notify the component that a submit has been fired from the page */ | ||
public submitTrigger$: Observable<boolean>; | ||
|
||
/** Observable to be passed to the form */ | ||
public submitForm$: Subject<boolean> = new Subject(); | ||
|
||
private readonly subscription = new Subscription(); | ||
|
||
/** The personal info form object model */ | ||
public personalInfo: PersonalInfo; | ||
/** The emergency contact form object model */ | ||
public emergencyContact: EmergencyContact; | ||
|
||
/** Form validators for personal info */ | ||
public personalInfoValidators: CustomFormValidation<PersonalInfo>; | ||
/** Form validators for emergency contact */ | ||
public emergencyContactValidators: CustomFormValidation<EmergencyContact>; | ||
|
||
/** The form control object bind to the personal info component */ | ||
public personalInfoFormControl: UntypedFormControl; | ||
/** The form control object bind to the emergency contact component */ | ||
public emergencyContactFormControl: UntypedFormControl; | ||
|
||
public firstSubmit = true; | ||
private readonly forbiddenName = 'Test'; | ||
|
||
constructor() { | ||
this.submitTrigger$ = this.submitForm$.asObservable(); | ||
this.translations = translations; | ||
this.personalInfo = { name: '', dateOfBirth: this.formatDate(Date.now()) }; | ||
this.personalInfoFormControl = new UntypedFormControl(this.personalInfo); | ||
this.emergencyContact = { name: '', phone: '', email: '' }; | ||
this.emergencyContactFormControl = new UntypedFormControl(this.emergencyContact); | ||
this.personalInfoValidators = { | ||
global: formsPresValidatorGlobal(this.forbiddenName, translations.globalForbiddenName, translations.globalForbiddenNameLong, { name: this.forbiddenName }), | ||
fields: { | ||
dateOfBirth: dateCustomValidator(translations.dateInThePast) | ||
} | ||
}; | ||
this.emergencyContactValidators = { | ||
global: formsPresValidatorGlobal(this.forbiddenName, translations.globalForbiddenName, translations.globalForbiddenNameLong, { name: this.forbiddenName }) | ||
}; | ||
} | ||
|
||
private formatDate(dateTime: number) { | ||
return formatDate(dateTime, 'yyyy-MM-dd', 'en-GB'); | ||
} | ||
|
||
public ngOnInit() { | ||
this.subscription.add(this.submitTrigger$.subscribe((_value) => this.submitAction())); | ||
} | ||
|
||
public ngOnDestroy() { | ||
this.subscription.unsubscribe(); | ||
} | ||
|
||
public submitForm() { | ||
this.submitForm$.next(true); | ||
} | ||
|
||
/** This will store the function to make the personal info form as dirty and touched */ | ||
public _markPersonalInfoInteraction: () => void = () => {}; | ||
/** This will store the function to make the emergency contact form as dirty and touched */ | ||
public _markEmergencyContactInteraction: () => void = () => {}; | ||
|
||
/** | ||
* Register the function to be called to mark the personal info form as touched and dirty | ||
* | ||
* @param fn | ||
*/ | ||
public registerPersonalInfoInteraction(fn: () => void) { | ||
this._markPersonalInfoInteraction = fn; | ||
} | ||
|
||
/** | ||
* Register the function to be called to mark the personal emergency contact form as touched and dirty | ||
* | ||
* @param fn | ||
*/ | ||
public registerEmergencyContactInteraction(fn: () => void) { | ||
this._markEmergencyContactInteraction = fn; | ||
} | ||
|
||
/** submit function */ | ||
public submitAction() { | ||
if (this.submitTrigger$ && this.firstSubmit) { | ||
this._markPersonalInfoInteraction(); | ||
this._markEmergencyContactInteraction(); | ||
this.firstSubmit = false; | ||
} | ||
const isValid = !this.personalInfoFormControl.errors && !this.emergencyContactFormControl.errors; | ||
if (isValid) { | ||
// eslint-disable-next-line no-console | ||
console.log('FORMS COMPONENT: personal info form status', this.personalInfoFormControl.status); | ||
// eslint-disable-next-line no-console | ||
console.log('FORMS COMPONENT: emergency contact form status', this.emergencyContactFormControl.status); | ||
// eslint-disable-next-line no-console | ||
console.log('FORMS COMPONENT: submit logic here', this.personalInfoFormControl.value, this.emergencyContactFormControl.value); | ||
} | ||
// eslint-disable-next-line no-console | ||
console.log('FORMS COMPONENT: submitted with value ', isValid); | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
apps/showcase/src/components/showcase/forms/forms-pres.localization.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"form.dateOfBirth.dateInThePast": { | ||
"description": "Validator for date of birth", | ||
"defaultValue": "Date of birth should be in the past" | ||
}, | ||
"form.globalForbiddenName": { | ||
"description": "This validator will check if the name will be the given config", | ||
"defaultValue": "Name cannot be { name }" | ||
}, | ||
"form.globalForbiddenName.long": { | ||
"description": "This validator will check if the name will be the given config", | ||
"defaultValue": "The value introduced for the name cannot be { name }" | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
apps/showcase/src/components/showcase/forms/forms-pres.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { ComponentFixture, TestBed } from '@angular/core/testing'; | ||
|
||
import { FormsPresComponent } from './forms-pres.component'; | ||
|
||
describe('FormsPresComponent', () => { | ||
let component: FormsPresComponent; | ||
let fixture: ComponentFixture<FormsPresComponent>; | ||
|
||
beforeEach(async () => { | ||
await TestBed.configureTestingModule({ | ||
imports: [FormsPresComponent] | ||
}) | ||
.compileComponents(); | ||
|
||
fixture = TestBed.createComponent(FormsPresComponent); | ||
Check failure on line 15 in apps/showcase/src/components/showcase/forms/forms-pres.spec.ts GitHub Actions / checks / test (ubuntu-latest)FormsPresComponent › should create
Check failure on line 15 in apps/showcase/src/components/showcase/forms/forms-pres.spec.ts GitHub Actions / checks / test (windows-latest)FormsPresComponent › should create
|
||
component = fixture.componentInstance; | ||
fixture.detectChanges(); | ||
}); | ||
|
||
it('should create', () => { | ||
expect(component).toBeTruthy(); | ||
}); | ||
}); |
3 changes: 3 additions & 0 deletions
3
apps/showcase/src/components/showcase/forms/forms-pres.style.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
o3r-forms-pres { | ||
// Your component custom SCSS | ||
} |
22 changes: 22 additions & 0 deletions
22
apps/showcase/src/components/showcase/forms/forms-pres.template.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<div class="card my-3"> | ||
<div class="row g-0"> | ||
<div class="col-6 align-items-center justify-center"> | ||
<o3r-forms-personal-info-pres | ||
[config]="{nameMaxLength: 5}" | ||
[customValidators]="personalInfoValidators" | ||
(registerInteraction)="registerPersonalInfoInteraction($event)" | ||
[formControl]="personalInfoFormControl"> | ||
</o3r-forms-personal-info-pres> | ||
</div> | ||
<div class="col-6 align-items-center justify-center"> | ||
<o3r-forms-emergency-contact-pres | ||
[customValidators]="emergencyContactValidators" | ||
(registerInteraction)="registerEmergencyContactInteraction($event)" | ||
[formControl]="emergencyContactFormControl"> | ||
</o3r-forms-emergency-contact-pres> | ||
</div> | ||
</div> | ||
<div class="row m-auto pb-3"> | ||
<button type="button" class="btn btn-primary" id="btn-submit" (click)="submitForm()">Submit</button> | ||
</div> | ||
</div> |
13 changes: 13 additions & 0 deletions
13
apps/showcase/src/components/showcase/forms/forms-pres.translation.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { Translation } from '@o3r/core'; | ||
|
||
export interface FormsPresTranslation extends Translation { | ||
dateInThePast: string; | ||
globalForbiddenName: string; | ||
globalForbiddenNameLong: string; | ||
} | ||
|
||
export const translations: FormsPresTranslation = { | ||
dateInThePast: 'form.dateOfBirth.dateInThePast', | ||
globalForbiddenName: 'form.globalForbiddenName', | ||
globalForbiddenNameLong: 'form.globalForbiddenName.long' | ||
}; |
Oops, something went wrong.