|
1 | 1 | /** @format */ |
2 | 2 |
|
3 | | -import { inject, Injectable, signal, effect } from "@angular/core"; |
| 3 | +import { inject, Injectable, signal } from "@angular/core"; |
4 | 4 | import { FormArray, FormBuilder, FormGroup, FormControl } from "@angular/forms"; |
5 | 5 | import { ProjectFormService } from "./project-form.service"; |
6 | 6 |
|
| 7 | +/** |
| 8 | + * Сервис для управления контактами проекта. |
| 9 | + * Предоставляет методы для добавления, редактирования, удаления ссылок, |
| 10 | + * а также очистки ошибок валидации. |
| 11 | + */ |
7 | 12 | @Injectable({ |
8 | 13 | providedIn: "root", |
9 | 14 | }) |
10 | 15 | export class ProjectContactsService { |
| 16 | + /** FormBuilder для создания FormGroup элементов */ |
11 | 17 | private readonly fb = inject(FormBuilder); |
| 18 | + /** Сервис для управления индексом редактируемой ссылки */ |
12 | 19 | private readonly projectFormService = inject(ProjectFormService); |
| 20 | + /** Сигнал для хранения списка ссылок (массив объектов) */ |
13 | 21 | public readonly linksItems = signal<any[]>([]); |
| 22 | + private initialized = false; |
14 | 23 |
|
15 | | - constructor() { |
16 | | - effect(() => { |
17 | | - const formArray = this.links; |
18 | | - if (formArray && formArray.length > 0) { |
19 | | - const currentSignalValue = this.linksItems(); |
20 | | - const formArrayValue = formArray.value; |
| 24 | + /** |
| 25 | + * Инициализирует сигнал linksItems из данных FormArray |
| 26 | + * Вызывается при первом обращении к данным |
| 27 | + */ |
| 28 | + private initializeLinksItems(linksFormArray: FormArray): void { |
| 29 | + if (this.initialized) return; |
21 | 30 |
|
22 | | - if (JSON.stringify(currentSignalValue) !== JSON.stringify(formArrayValue)) { |
23 | | - this.linksItems.set(formArrayValue); |
24 | | - } |
25 | | - } |
26 | | - }); |
| 31 | + if (linksFormArray && linksFormArray.length > 0) { |
| 32 | + // Синхронизируем сигнал с данными из FormArray |
| 33 | + this.linksItems.set(linksFormArray.value); |
| 34 | + } |
| 35 | + this.initialized = true; |
27 | 36 | } |
28 | 37 |
|
| 38 | + /** |
| 39 | + * Принудительно синхронизирует сигнал с FormArray |
| 40 | + * Полезно вызывать после загрузки данных с сервера |
| 41 | + */ |
| 42 | + public syncLinksItems(linksFormArray: FormArray): void { |
| 43 | + if (linksFormArray) { |
| 44 | + this.linksItems.set(linksFormArray.value); |
| 45 | + } |
| 46 | + } |
| 47 | + |
| 48 | + /** |
| 49 | + * Получает основную форму проекта |
| 50 | + */ |
29 | 51 | private get projectForm(): FormGroup { |
30 | 52 | return this.projectFormService.getForm(); |
31 | 53 | } |
32 | 54 |
|
| 55 | + /** |
| 56 | + * Получает FormArray ссылок |
| 57 | + */ |
33 | 58 | public get links(): FormArray { |
34 | 59 | return this.projectForm.get("links") as FormArray; |
35 | 60 | } |
36 | 61 |
|
| 62 | + /** |
| 63 | + * Получает FormControl для поля ввода ссылки |
| 64 | + */ |
37 | 65 | public get link(): FormControl { |
38 | 66 | return this.projectForm.get("link") as FormControl; |
39 | 67 | } |
40 | 68 |
|
41 | 69 | /** |
42 | | - * Принудительная синхронизация сигнала с FormArray |
43 | | - * Вызывается после загрузки данных проекта |
| 70 | + * Добавляет новую ссылку или сохраняет изменения существующей. |
| 71 | + * @param linksFormArray FormArray, содержащий формы ссылок |
| 72 | + * @param projectForm основная форма проекта (FormGroup) |
44 | 73 | */ |
45 | | - public syncLinksItems(): void { |
46 | | - const linksFormArray = this.links; |
47 | | - if (linksFormArray && linksFormArray.length > 0) { |
48 | | - this.linksItems.set(linksFormArray.value); |
49 | | - } else { |
50 | | - this.linksItems.set([]); |
51 | | - } |
52 | | - } |
| 74 | + public addLink(linksFormArray: FormArray, projectForm: FormGroup): void { |
| 75 | + // Инициализируем сигнал при первом вызове |
| 76 | + this.initializeLinksItems(linksFormArray); |
53 | 77 |
|
54 | | - public addLink(): void { |
55 | | - const linkValue = this.link?.value; |
| 78 | + // Считываем вводимые данные |
| 79 | + const linkValue = projectForm.get("link")?.value; |
56 | 80 |
|
| 81 | + // Проверяем, что поле не пустое и содержит валидный URL |
57 | 82 | if ( |
58 | 83 | !linkValue || |
59 | 84 | !linkValue.trim() || |
60 | | - !linkValue.includes("https://") || |
61 | | - !linkValue.includes("http://") |
| 85 | + (!linkValue.includes("https://") && !linkValue.includes("http://")) |
62 | 86 | ) { |
63 | | - return; |
| 87 | + return; // Выходим из функции, если поле пустое или невалидное |
64 | 88 | } |
65 | 89 |
|
66 | 90 | const trimmedLink = linkValue.trim(); |
67 | | - const editIdx = this.projectFormService.editIndex(); |
68 | 91 |
|
| 92 | + // Проверяем, редактируется ли существующая ссылка |
| 93 | + const editIdx = this.projectFormService.editIndex(); |
69 | 94 | if (editIdx !== null) { |
70 | | - // Режим редактирования |
71 | | - this.links.at(editIdx).setValue(trimmedLink); |
| 95 | + // Обновляем массив сигналов и соответствующий контрол в FormArray |
72 | 96 | this.linksItems.update(items => { |
73 | 97 | const updated = [...items]; |
74 | | - updated[editIdx] = trimmedLink; |
| 98 | + updated[editIdx] = trimmedLink.value; |
75 | 99 | return updated; |
76 | 100 | }); |
| 101 | + linksFormArray.at(editIdx).patchValue(trimmedLink.value); |
| 102 | + // Сбрасываем индекс редактирования |
77 | 103 | this.projectFormService.editIndex.set(null); |
78 | 104 | } else { |
79 | | - // Добавление нового элемента |
80 | | - this.links.push(this.fb.control(trimmedLink)); |
81 | | - this.linksItems.update(items => [...items, trimmedLink]); |
| 105 | + // Добавляем новую ссылку в сигнал и FormArray |
| 106 | + this.linksItems.update(items => [...items, trimmedLink.value]); |
| 107 | + linksFormArray.push(trimmedLink); |
82 | 108 | } |
83 | 109 |
|
84 | | - // Очищаем поле ввода |
85 | | - this.link?.reset(); |
86 | | - this.link?.setValue(""); |
| 110 | + // Очищаем поле ввода формы проекта |
| 111 | + projectForm.get("link")?.reset(); |
| 112 | + projectForm.get("link")?.setValue(""); |
87 | 113 | } |
88 | 114 |
|
89 | | - public editLink(index: number): void { |
90 | | - const value = this.links.value[index]; |
91 | | - this.projectForm.patchValue({ link: value }); |
| 115 | + /** |
| 116 | + * Инициализирует редактирование существующей ссылки. |
| 117 | + * @param index индекс ссылки в списке |
| 118 | + * @param linksFormArray FormArray ссылок |
| 119 | + * @param projectForm основная форма проекта |
| 120 | + */ |
| 121 | + public editLink(index: number, linksFormArray: FormArray, projectForm: FormGroup): void { |
| 122 | + // Инициализируем сигнал при необходимости |
| 123 | + this.initializeLinksItems(linksFormArray); |
| 124 | + |
| 125 | + // Используем данные из FormArray как источник истины |
| 126 | + const source = linksFormArray.value[index]; |
| 127 | + |
| 128 | + // Заполняем поле формы проекта для редактирования |
| 129 | + projectForm.patchValue({ |
| 130 | + link: source?.link || "", |
| 131 | + }); |
| 132 | + // Устанавливаем текущий индекс редактирования в сервисе |
92 | 133 | this.projectFormService.editIndex.set(index); |
93 | 134 | } |
94 | 135 |
|
95 | | - public removeLink(index: number): void { |
96 | | - this.links.removeAt(index); |
| 136 | + /** |
| 137 | + * Удаляет ссылку по указанному индексу. |
| 138 | + * @param index индекс удаляемой ссылки |
| 139 | + * @param linksFormArray FormArray ссылок |
| 140 | + */ |
| 141 | + public removeLink(index: number, linksFormArray: FormArray): void { |
| 142 | + // Удаляем из сигнала и из FormArray |
97 | 143 | this.linksItems.update(items => items.filter((_, i) => i !== index)); |
| 144 | + linksFormArray.removeAt(index); |
| 145 | + } |
| 146 | + |
| 147 | + /** |
| 148 | + * Сбрасывает все ошибки валидации во всех контролах FormArray ссылок. |
| 149 | + * @param links FormArray ссылок |
| 150 | + */ |
| 151 | + public clearAllLinksErrors(links: FormArray): void { |
| 152 | + links.controls.forEach(control => { |
| 153 | + if (control instanceof FormGroup) { |
| 154 | + Object.keys(control.controls).forEach(key => { |
| 155 | + control.get(key)?.setErrors(null); |
| 156 | + }); |
| 157 | + } |
| 158 | + }); |
98 | 159 | } |
99 | 160 |
|
| 161 | + /** |
| 162 | + * Сбрасывает состояние сервиса |
| 163 | + * Полезно при смене проекта или очистке формы |
| 164 | + */ |
100 | 165 | public reset(): void { |
101 | 166 | this.linksItems.set([]); |
| 167 | + this.initialized = false; |
102 | 168 | } |
103 | 169 | } |
0 commit comments