Kaynağa Gözat

[update]

再描画処理
poohr 3 hafta önce
ebeveyn
işleme
876b7da25e

+ 5
- 1
src/app/pages/event/event.html Dosyayı Görüntüle

76
             <div>状態</div>
76
             <div>状態</div>
77
           </div>
77
           </div>
78
 
78
 
79
-          @if (filteredEventTargets.length > 0) {
79
+          @if (isLoading) {
80
+            <div class="empty-message">
81
+              読み込み中です。
82
+            </div>
83
+          } @else if (filteredEventTargets.length > 0) {
80
             @for (target of filteredEventTargets; track target.id) {
84
             @for (target of filteredEventTargets; track target.id) {
81
               <div class="event-table-row">
85
               <div class="event-table-row">
82
                 <div>
86
                 <div>

+ 54
- 10
src/app/pages/event/event.ts Dosyayı Görüntüle

1
-import { Component, OnInit } from '@angular/core';
1
+import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
2
 import { FormsModule } from '@angular/forms';
2
 import { FormsModule } from '@angular/forms';
3
 import { DankaService } from '../../services/dankaService';
3
 import { DankaService } from '../../services/dankaService';
4
 import { FamilyService } from '../../services/family-service';
4
 import { FamilyService } from '../../services/family-service';
15
 })
15
 })
16
 export class EventPage implements OnInit{
16
 export class EventPage implements OnInit{
17
   eventTargets: EventTarget[] = [];
17
   eventTargets: EventTarget[] = [];
18
+  isLoading = true;
18
   targetYear: number = new Date().getFullYear();
19
   targetYear: number = new Date().getFullYear();
19
   selectedEventType: EventType | 'all' = 'all';
20
   selectedEventType: EventType | 'all' = 'all';
20
   selectedStatus: EventStatus | 'all' = 'all';
21
   selectedStatus: EventStatus | 'all' = 'all';
38
     { label: '未案内', value: '未案内' },
39
     { label: '未案内', value: '未案内' },
39
     { label: '案内済', value: '案内済' },
40
     { label: '案内済', value: '案内済' },
40
   ];
41
   ];
42
+  private eventRequestId = 0;
41
 
43
 
42
   constructor(
44
   constructor(
43
     private dankaService: DankaService,
45
     private dankaService: DankaService,
44
     private familyService: FamilyService,
46
     private familyService: FamilyService,
45
     private eventService: EventService,
47
     private eventService: EventService,
48
+    private cdr: ChangeDetectorRef,
46
   ) { }
49
   ) { }
47
 
50
 
48
   ngOnInit(): void {
51
   ngOnInit(): void {
54
   }
57
   }
55
 
58
 
56
   async createEventTargetList(): Promise<void> {
59
   async createEventTargetList(): Promise<void> {
57
-    this.eventTargets = [];
60
+    const requestId = ++this.eventRequestId;
61
+    const targetYear = this.targetYear;
62
+    const selectedEventType = this.selectedEventType;
63
+    const eventTargets: EventTarget[] = [];
64
+
65
+    if (this.eventTargets.length === 0) {
66
+      this.isLoading = true;
67
+      this.cdr.detectChanges();
68
+    }
58
 
69
 
59
     const families = await this.familyService.getFamilyList();
70
     const families = await this.familyService.getFamilyList();
60
 
71
 
62
       const birthDate = this.parseDate(family.birthDate);
73
       const birthDate = this.parseDate(family.birthDate);
63
       if (!birthDate) continue;
74
       if (!birthDate) continue;
64
 
75
 
65
-      const age = this.targetYear - birthDate.getFullYear();
76
+      const age = targetYear - birthDate.getFullYear();
66
       const eventTypes = this.getEventTypes(age);
77
       const eventTypes = this.getEventTypes(age);
67
       if (eventTypes.length === 0) continue;
78
       if (eventTypes.length === 0) continue;
68
 
79
 
71
 
82
 
72
       for (const eventType of eventTypes) {
83
       for (const eventType of eventTypes) {
73
         if (
84
         if (
74
-          this.selectedEventType !== 'all' &&
75
-          this.selectedEventType !== eventType
85
+          selectedEventType !== 'all' &&
86
+          selectedEventType !== eventType
76
         ) {
87
         ) {
77
           continue;
88
           continue;
78
         }
89
         }
82
         const defaultStatus: EventStatus =
93
         const defaultStatus: EventStatus =
83
           Number(family.id) % 2 === 0 ? '案内済' : '未案内';
94
           Number(family.id) % 2 === 0 ? '案内済' : '未案内';
84
 
95
 
85
-        this.eventTargets.push({
96
+        eventTargets.push({
86
           id,
97
           id,
87
           dankaId: family.dankaId,
98
           dankaId: family.dankaId,
88
           name: family.name,
99
           name: family.name,
89
           furigana: family.furigana,
100
           furigana: family.furigana,
90
           householdName: danka?.householdName ?? '不明',
101
           householdName: danka?.householdName ?? '不明',
91
           relationship: family.relationship || '未登録',
102
           relationship: family.relationship || '未登録',
92
-          birthDate: family.birthDate,
103
+          birthDate: this.formatDateForValue(birthDate),
93
           age,
104
           age,
94
           eventType,
105
           eventType,
95
           note: family.note,
106
           note: family.note,
98
       }
109
       }
99
     }
110
     }
100
 
111
 
101
-    this.eventTargets.sort(
112
+    if (requestId !== this.eventRequestId) {
113
+      return;
114
+    }
115
+
116
+    this.eventTargets = eventTargets.sort(
102
       (a, b) =>
117
       (a, b) =>
103
         this.getEventSortOrder(a.eventType) -
118
         this.getEventSortOrder(a.eventType) -
104
         this.getEventSortOrder(b.eventType) ||
119
         this.getEventSortOrder(b.eventType) ||
105
         a.age - b.age ||
120
         a.age - b.age ||
106
         a.name.localeCompare(b.name, 'ja'),
121
         a.name.localeCompare(b.name, 'ja'),
107
     );
122
     );
123
+    this.isLoading = false;
124
+    this.cdr.detectChanges();
108
   }
125
   }
109
 
126
 
110
   changeEventType(eventType: EventType | 'all'): void {
127
   changeEventType(eventType: EventType | 'all'): void {
128
           target.eventType,
145
           target.eventType,
129
           target.note,
146
           target.note,
130
           target.status,
147
           target.status,
131
-        ].some((value) => value.includes(keyword));
148
+        ].some((value) => this.includesKeyword(value, keyword));
132
 
149
 
133
       return matchesStatus && matchesKeyword;
150
       return matchesStatus && matchesKeyword;
134
     });
151
     });
162
     return ['稚児行列', '七五三', '成人式', '米寿'].indexOf(eventType);
179
     return ['稚児行列', '七五三', '成人式', '米寿'].indexOf(eventType);
163
   }
180
   }
164
 
181
 
165
-  private parseDate(value: string): Date | null {
182
+  private parseDate(value: unknown): Date | null {
183
+    if (!value) {
184
+      return null;
185
+    }
186
+
187
+    if (value instanceof Date) {
188
+      return value;
189
+    }
190
+
191
+    if (typeof value === 'object' && 'toDate' in value && typeof value.toDate === 'function') {
192
+      return value.toDate();
193
+    }
194
+
195
+    if (typeof value !== 'string') {
196
+      return null;
197
+    }
198
+
166
     const [year, month, day] = value.split('-').map(Number);
199
     const [year, month, day] = value.split('-').map(Number);
167
     if (!year || !month || !day) {
200
     if (!year || !month || !day) {
168
       return null;
201
       return null;
169
     }
202
     }
170
     return new Date(year, month - 1, day);
203
     return new Date(year, month - 1, day);
171
   }
204
   }
205
+
206
+  private includesKeyword(value: unknown, keyword: string): boolean {
207
+    return String(value ?? '').includes(keyword);
208
+  }
209
+
210
+  private formatDateForValue(date: Date): string {
211
+    const year = date.getFullYear();
212
+    const month = String(date.getMonth() + 1).padStart(2, '0');
213
+    const day = String(date.getDate()).padStart(2, '0');
214
+    return `${year}-${month}-${day}`;
215
+  }
172
 }
216
 }

+ 5
- 1
src/app/pages/memorial-list/memorial-list.html Dosyayı Görüntüle

69
             <div>詳細</div>
69
             <div>詳細</div>
70
           </div>
70
           </div>
71
 
71
 
72
-          @if (filteredMemorialList.length > 0) {
72
+          @if (isLoading) {
73
+            <div class="empty-message">
74
+              読み込み中です。
75
+            </div>
76
+          } @else if (filteredMemorialList.length > 0) {
73
             @for (memorial of filteredMemorialList; track memorial.id) {
77
             @for (memorial of filteredMemorialList; track memorial.id) {
74
               <div class="memorial-table-row">
78
               <div class="memorial-table-row">
75
                 <div class="person-name">
79
                 <div class="person-name">

+ 69
- 13
src/app/pages/memorial-list/memorial-list.ts Dosyayı Görüntüle

1
-import { Component, OnInit } from '@angular/core';
1
+import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
2
 import { ActivatedRoute, Router, RouterLink } from '@angular/router';
2
 import { ActivatedRoute, Router, RouterLink } from '@angular/router';
3
 import { Memorial } from '../../models/memorial';
3
 import { Memorial } from '../../models/memorial';
4
 import { DankaService } from '../../services/dankaService';
4
 import { DankaService } from '../../services/dankaService';
15
 })
15
 })
16
 export class MemorialList implements OnInit{
16
 export class MemorialList implements OnInit{
17
   memorialList: Memorial[] = [];
17
   memorialList: Memorial[] = [];
18
+  isLoading = true;
18
   targetYear: number = new Date().getFullYear();
19
   targetYear: number = new Date().getFullYear();
19
   selectedMemorialType = 'all';
20
   selectedMemorialType = 'all';
20
   searchKeyword = '';
21
   searchKeyword = '';
22
+  private memorialRequestId = 0;
21
   yearOptions: number[] = [
23
   yearOptions: number[] = [
22
     this.targetYear - 1,
24
     this.targetYear - 1,
23
     this.targetYear,
25
     this.targetYear,
45
   constructor(
47
   constructor(
46
     private dankaService: DankaService,
48
     private dankaService: DankaService,
47
     private kakochoService: KakochoService,
49
     private kakochoService: KakochoService,
50
+    private cdr: ChangeDetectorRef,
48
   ) {
51
   ) {
49
 
52
 
50
   }
53
   }
57
   }
60
   }
58
 
61
 
59
   async createMemorialList(): Promise<void> {
62
   async createMemorialList(): Promise<void> {
60
-    this.memorialList = [];
63
+    const requestId = ++this.memorialRequestId;
64
+    const targetYear = this.targetYear;
65
+    const selectedMemorialType = this.selectedMemorialType;
66
+    const memorialList: Memorial[] = [];
67
+
68
+    if (this.memorialList.length === 0) {
69
+      this.isLoading = true;
70
+      this.cdr.detectChanges();
71
+    }
61
 
72
 
62
     const kakochoList = await this.kakochoService.getKakochoList();
73
     const kakochoList = await this.kakochoService.getKakochoList();
63
 
74
 
64
     for (const kakocho of kakochoList) {
75
     for (const kakocho of kakochoList) {
65
-      const deathYear = Number(kakocho.deathDate.slice(0, 4));
66
-      const yearDiff = this.targetYear - deathYear;
76
+      const deathDate = this.parseDate(kakocho.deathDate);
77
+      if (!deathDate) continue;
78
+
79
+      const deathYear = deathDate.getFullYear();
80
+      const yearDiff = targetYear - deathYear;
67
 
81
 
68
       const memorialType = this.getMemorialType(yearDiff);
82
       const memorialType = this.getMemorialType(yearDiff);
69
       if (memorialType === '') continue;
83
       if (memorialType === '') continue;
70
 
84
 
71
       if (
85
       if (
72
-        this.selectedMemorialType !== 'all' &&
73
-        this.selectedMemorialType !== memorialType
86
+        selectedMemorialType !== 'all' &&
87
+        selectedMemorialType !== memorialType
74
       ) {
88
       ) {
75
         continue;
89
         continue;
76
       }
90
       }
85
         kaimyo: kakocho.kaimyo,
99
         kaimyo: kakocho.kaimyo,
86
         relationship: kakocho.relationship,
100
         relationship: kakocho.relationship,
87
         householdName: danka?.householdName ?? '不明',
101
         householdName: danka?.householdName ?? '不明',
88
-        deathDate: kakocho.deathDate,
102
+        deathDate: this.formatDateForValue(deathDate),
89
         memorialType,
103
         memorialType,
90
         note: kakocho.note,
104
         note: kakocho.note,
91
       };
105
       };
92
 
106
 
93
-      this.memorialList.push(memorialTarget);
107
+      memorialList.push(memorialTarget);
94
     }
108
     }
95
 
109
 
96
-    this.memorialList.sort((a, b) => {
110
+    if (requestId !== this.memorialRequestId) {
111
+      return;
112
+    }
113
+
114
+    this.memorialList = memorialList.sort((a, b) => {
97
       const deathDateA = new Date(a.deathDate).getTime();
115
       const deathDateA = new Date(a.deathDate).getTime();
98
       const deathDateB = new Date(b.deathDate).getTime();
116
       const deathDateB = new Date(b.deathDate).getTime();
99
 
117
 
103
 
121
 
104
       return a.name.localeCompare(b.name, 'ja');
122
       return a.name.localeCompare(b.name, 'ja');
105
     });
123
     });
124
+    this.isLoading = false;
125
+    this.cdr.detectChanges();
106
   }
126
   }
107
 
127
 
108
   changeMemorialType(memorialType: string): void {
128
   changeMemorialType(memorialType: string): void {
126
         memorial.householdName,
146
         memorial.householdName,
127
         memorial.memorialType,
147
         memorial.memorialType,
128
         memorial.note,
148
         memorial.note,
129
-      ].some((value) => value.includes(keyword)),
149
+      ].some((value) => this.includesKeyword(value, keyword)),
130
     );
150
     );
131
   }
151
   }
132
 
152
 
153
+  private includesKeyword(value: unknown, keyword: string): boolean {
154
+    return String(value ?? '').includes(keyword);
155
+  }
156
+
133
   formatDeathDate(deathDate: string): string {
157
   formatDeathDate(deathDate: string): string {
134
-    const [, month, day] = deathDate.split('-').map(Number);
135
-    if (!month || !day) {
158
+    const date = this.parseDate(deathDate);
159
+    if (!date) {
136
       return '未登録';
160
       return '未登録';
137
     }
161
     }
138
 
162
 
139
-    return `${month}月${day}日`;
163
+    return `${date.getMonth() + 1}月${date.getDate()}日`;
140
   }
164
   }
141
 
165
 
142
   getMemorialType(yearDiff: number) {
166
   getMemorialType(yearDiff: number) {
173
         return '';
197
         return '';
174
     }
198
     }
175
   }
199
   }
200
+
201
+  private parseDate(value: unknown): Date | null {
202
+    if (!value) {
203
+      return null;
204
+    }
205
+
206
+    if (value instanceof Date) {
207
+      return value;
208
+    }
209
+
210
+    if (typeof value === 'object' && 'toDate' in value && typeof value.toDate === 'function') {
211
+      return value.toDate();
212
+    }
213
+
214
+    if (typeof value !== 'string') {
215
+      return null;
216
+    }
217
+
218
+    const [year, month, day] = value.split('-').map(Number);
219
+    if (!year || !month || !day) {
220
+      return null;
221
+    }
222
+
223
+    return new Date(year, month - 1, day);
224
+  }
225
+
226
+  private formatDateForValue(date: Date): string {
227
+    const year = date.getFullYear();
228
+    const month = String(date.getMonth() + 1).padStart(2, '0');
229
+    const day = String(date.getDate()).padStart(2, '0');
230
+    return `${year}-${month}-${day}`;
231
+  }
176
 }
232
 }

+ 4
- 1
src/app/pages/search/search.ts Dosyayı Görüntüle

1
-import { Component, OnInit } from '@angular/core';
1
+import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
2
 import { FormsModule } from '@angular/forms';
2
 import { FormsModule } from '@angular/forms';
3
 import { ActivatedRoute, RouterLink } from '@angular/router';
3
 import { ActivatedRoute, RouterLink } from '@angular/router';
4
 import { Danka } from '../../models/danka';
4
 import { Danka } from '../../models/danka';
37
     private familyService: FamilyService,
37
     private familyService: FamilyService,
38
     private kakochoService: KakochoService,
38
     private kakochoService: KakochoService,
39
     private route: ActivatedRoute,
39
     private route: ActivatedRoute,
40
+    private cdr: ChangeDetectorRef,
40
   ) { }
41
   ) { }
41
 
42
 
42
   ngOnInit(): void {
43
   ngOnInit(): void {
153
     this.familyResults = [];
154
     this.familyResults = [];
154
     this.kakochoResults = [];
155
     this.kakochoResults = [];
155
     this.totalResultCount = 0;
156
     this.totalResultCount = 0;
157
+    this.cdr.detectChanges();
156
   }
158
   }
157
 
159
 
158
   private includesKeyword(value: unknown, keyword: string): boolean {
160
   private includesKeyword(value: unknown, keyword: string): boolean {
180
       dankaResults.length +
182
       dankaResults.length +
181
       familyResults.length +
183
       familyResults.length +
182
       kakochoResults.length;
184
       kakochoResults.length;
185
+    this.cdr.detectChanges();
183
   }
186
   }
184
 }
187
 }

Loading…
İptal
Kaydet