I have this simple directive in Angular for counting nested arrays:
import { AfterViewInit, Directive, Input, OnDestroy } from '@angular/core';
@Directive({
selector: '[rhsNestedArrayCounter]',
exportAs: 'rhsNestedArrayCounter'
})
export class NestedArrayCounterDirective implements AfterViewInit, OnDestroy {
static nextIndex = -1;
@Input() appCounter: number | string = '';
index;
constructor() {
this.index = NestedArrayCounterDirective.nextIndex++;
}
ngAfterViewInit(): void {
if (typeof this.appCounter === 'string') {
return;
}
NestedArrayCounterDirective.nextIndex = this.appCounter;
}
ngOnDestroy(): void {
NestedArrayCounterDirective.nextIndex = -1;
}
}
And I used it in my html like this, lets call it FormComponent:
<form [formGroup]="detailForm" class="form-container">
<ng-container rhsNestedArrayCounter="0">
<div *ngFor="let section of sections; let sectionIndex = index">
<div class="section">
<div class="section-content">
<h3>{{ section.title }}</h3>
<div formArrayName="responses">
<div
*ngFor="
let questionWithAnswer of section.questionsWithAnswers;
let questionIndex = index
">
<div class="question">
<div
rhsNestedArrayCounter
#counter="rhsNestedArrayCounter"
class="question-content"
[formGroupName]="counter.index">
<h4>
{{
questionWithAnswer.questionDescription
| textReplacer: '#EmployeeName':employeeName
}}
</h4>
<div>
<mat-form-field
color="accent"
id="sectionAnswer-{{ counter.index }}">
<textarea
matInput
placeholder="Answer"
formControlName="text"
name="text"
id="sectionAnswerInput-{{ counter.index }}"
[required]="questionWithAnswer.isRequired"
cdkTextareaAutosize
#autosize="cdkTextareaAutosize"
cdkAutosizeMinRows="2"
cdkAutosizeMaxRows="6"></textarea>
<mat-error
*ngIf="responses.controls[counter.index].get('text')?.errors?.['required']"
id="sectionAnswerError"
>Required field</mat-error
>
<mat-error
*ngIf="responses.controls[counter.index].get('text')?.errors?.['minlength']"
id="sectionAnswerMinLengthError"
>Min 2 characters</mat-error
>
<mat-error
*ngIf="responses.controls[counter.index].get('text')?.errors?.['maxlength']"
id="sectionAnswerMaxLengthError"
>Max 6000 characters</mat-error
>
<mat-error
*ngIf="responses.controls[counter.index].get('text')?.errors?.['pattern']"
id="sectionAnswerSpecialCharactersPatternError"
>Only letters, numbers, and some punctuation marks are
allowed.</mat-error
>
</mat-form-field>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</ng-container>
</form>
When I used FormComponent in my PageComponent one time everything worked fine, now I have a PageTwoComponent and I need to use the FormComponent more than one time, and because this counter is global I got errors about the index. For example the form have 15 questions, and I got the error of index 16 because when start count the second FormComponent it start from 16 because the first FormComponent finish in 15 (using two times the FormComponent in a Parent component).
So I thought maybe used a dictionary in the directive where I have for every component a unique index for every name, but I don't know how to implement this, or maybe another idea (I'm a C# developer) any help is welcome.
If I understood it correctly, you want such usage rhsNestedArrayCounter="0"
to restore counter to index 0, and then just rhsNestedArrayCounter
to save current index and increment it.
If that is so then you should firstly use ngOnInit hook rather than ngAfterViewInit, and, considering your usage rhsNestedArrayCounter="0"
0 would be passed as a string "0" here.
let counterValue = -1; // would work almost the same as the static prop
class NestedArrayCounterDirective {
@Input() rhsNestedArrayCounter: number | string = ''; // name of the input matters.
index: number;
ngOnInit() {
if(this.rhs) {
counterValue = +this.rhs;
} else {
this.index = counterValue++;
}
}
ngOnDestroy() { counterValue = -1;}
}
also to not make hacks with initializing and clearing the "counter" in directives, you can use a simple service for that counter, which you'll just put in the parent component's providers
section
@Injectable()
class CounterService {
private val = 0;
public getAndIncrement() {return this.val++;}
}
and that service should basically created "scoped" version of counter for the FormComponent and it should resolve the problem for you
provide service like here
@Component({providers: [CounterService],...}) cass FormComponent {}
and use increment in the directive like here
class NestedArrayCounterDirective {
index = inject(CounerService).getAndIncrement();
}
even without any hooks it should work correctly