Skip to content

Commit 7efd419

Browse files
committed
use FormRecord vs FormGroup (for dynamic fields), update UI for user management, remove unnecessary change detection in settings.component.ts, no more "untyped" forms
1 parent 7a7cf2c commit 7efd419

File tree

2 files changed

+40
-47
lines changed

2 files changed

+40
-47
lines changed

src/frontend/src/app/settings/settings.component.html

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
</div>
3838
<div class="my-2 d-flex justify-content-between">
3939
<button type="button" class="btn btn-sm btn-outline-primary position-relative" (click)="saveAndVerifyJackettIndexers()">
40-
<ngx-loading [show]="isVeryingJackettIndexers"></ngx-loading>
40+
<ngx-loading [show]="isVerifyingJackettIndexers"></ngx-loading>
4141
Save & Verify Indexers
4242
<span class="oi oi-pulse"></span>
4343
</button>
@@ -149,31 +149,31 @@
149149
<ngx-loading [show]="isLoadingUsers"></ngx-loading>
150150
<div *ngIf="users">
151151
<div *ngFor="let control of form.controls.users.controls; let i=index" [formGroupName]="i">
152-
<div class="d-flex justify-content-around">
153-
<div class="my-2 me-1">
154-
<input type="text" class="form-control" [id]="i+'username'" formControlName="username" required placeholder="username">
155-
</div>
156-
<div class="my-2 me-1">
157-
<ng-container *ngIf="control.value.id">
158-
<input type="password" class="form-control" [id]="i+'password'" formControlName="password" placeholder="new password">
159-
<small class="form-text text-muted">Leave blank to keep existing password</small>
160-
</ng-container>
161-
<ng-container *ngIf="!control.value.id">
162-
<input type="password" class="form-control" [id]="i+'password'" formControlName="password" placeholder="password" required>
163-
</ng-container>
164-
</div>
165-
<div class="my-2 me-1">
166-
<button type="button" class="btn btn-sm btn-success" (click)="saveUser(i)"><span class="oi oi-check"></span></button>
167-
</div>
168-
<div class="my-2">
169-
<button type="button" class="btn btn-sm btn-danger" [disabled]="!canDeleteUser(i)" (click)="removeUser(i)"><span class="oi oi-minus"></span></button>
170-
</div>
152+
<!-- username -->
153+
<div class="my-1">
154+
<input type="text" class="form-control" [id]="i+'username'" formControlName="username" required placeholder="username">
155+
</div>
156+
<!-- password -->
157+
<div class="my-1">
158+
<ng-container *ngIf="control.value.id">
159+
<input type="password" class="form-control" [id]="i+'password'" formControlName="password" placeholder="new password">
160+
<small class="form-text text-muted">Leave blank to keep existing password</small>
161+
</ng-container>
162+
<ng-container *ngIf="!control.value.id">
163+
<input type="password" class="form-control" [id]="i+'password'" formControlName="password" placeholder="password" required>
164+
</ng-container>
165+
</div>
166+
<!-- add/remove -->
167+
<div class="d-flex justify-content-end">
168+
<button type="button" class="btn btn-sm btn-outline-success me-2" (click)="saveUser(i)"><span class="oi oi-check"></span></button>
169+
<button type="button" class="btn btn-sm btn-outline-danger" [disabled]="!canDeleteUser(i)" (click)="removeUser(i)"><span class="oi oi-minus"></span></button>
171170
</div>
172171
<hr>
173172
</div>
174173
</div>
174+
<!-- new user -->
175175
<div class="d-flex justify-content-end">
176-
<button type="button" class="btn btn-xs btn-sm btn-success" (click)="addUser()"><span class="oi oi-plus"></span></button>
176+
<button type="button" class="btn btn-sm btn-sm btn-outline-success" (click)="addUser()"><span class="oi oi-plus"></span></button>
177177
</div>
178178
</div>
179179
</div>

src/frontend/src/app/settings/settings.component.ts

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import {EMPTY} from 'rxjs';
2-
import {ChangeDetectorRef} from '@angular/core';
32
import {ToastrService} from 'ngx-toastr';
43
import {ApiService} from '../api.service';
5-
import {UntypedFormArray, UntypedFormBuilder, UntypedFormControl, Validators} from '@angular/forms';
6-
import {Component, OnInit, AfterContentChecked} from '@angular/core';
4+
import {FormArray, FormBuilder, FormControl, Validators, FormRecord} from '@angular/forms';
5+
import {Component, OnInit} from '@angular/core';
76
import {concat, Observable, Subscription} from 'rxjs';
87
import {tap} from 'rxjs/operators';
98
import {NgbModal} from "@ng-bootstrap/ng-bootstrap";
@@ -14,30 +13,24 @@ import {QualityProfilesComponent} from "./quality-profiles.component";
1413
templateUrl: './settings.component.html',
1514
styleUrls: ['./settings.component.css']
1615
})
17-
export class SettingsComponent implements OnInit, AfterContentChecked {
16+
export class SettingsComponent implements OnInit {
1817
public users: any[];
19-
public form;
18+
public form: FormRecord<any>;
2019
public isSaving = false;
2120
public isLoading = false;
22-
public isVeryingJackettIndexers = false;
21+
public isVerifyingJackettIndexers = false;
2322
public isLoadingUsers = false;
2423
public gitCommit = '';
2524
public authenticateOpenSubtitles$: Subscription;
2625

2726
constructor(
2827
public apiService: ApiService,
2928
public toastr: ToastrService,
30-
public fb: UntypedFormBuilder,
31-
public changeDectorRef: ChangeDetectorRef,
29+
public fb: FormBuilder,
3230
public modalService: NgbModal,
3331
) {
3432
}
3533

36-
ngAfterContentChecked() {
37-
// handles form "required" dynamically changing after lifecycle check
38-
this.changeDectorRef.detectChanges();
39-
}
40-
4134
ngOnInit() {
4235
const settings = this.apiService.settings || {};
4336
this.form = this.fb.group({
@@ -60,7 +53,7 @@ export class SettingsComponent implements OnInit, AfterContentChecked {
6053
'exclusions': [settings['keyword_search_filters'] ? Object.keys(settings['keyword_search_filters']) : []],
6154
'enable_video_detection': [settings['enable_video_detection'], Validators.required],
6255
'language': [settings['language'], Validators.required],
63-
'users': new UntypedFormArray([]),
56+
'users': new FormArray([]),
6457
'apprise_notification_url': [settings['apprise_notification_url']],
6558
'preferred_media_category': [settings['preferred_media_category'], Validators.required],
6659
'stuck_download_handling_enabled': [settings['stuck_download_handling_enabled'], Validators.required],
@@ -76,9 +69,9 @@ export class SettingsComponent implements OnInit, AfterContentChecked {
7669
password: '',
7770
};
7871
Object.keys(user).forEach((key) => {
79-
controls[key] = new UntypedFormControl(user[key]);
72+
controls[key] = new FormControl(user[key]);
8073
});
81-
this.form.get('users').insert(0, this.fb.group(controls));
74+
this.form.controls.users.insert(0, this.fb.group(controls));
8275
});
8376
}, (error) => {
8477
this.toastr.error('An unknown error occurred loading users');
@@ -97,7 +90,7 @@ export class SettingsComponent implements OnInit, AfterContentChecked {
9790
}
9891

9992
public hasExclusions(): boolean {
100-
const exclusions = this.form.get('exclusions').value;
93+
const exclusions = this.form.controls.exclusions.value;
10194
return exclusions && exclusions.length;
10295
}
10396

@@ -126,7 +119,7 @@ export class SettingsComponent implements OnInit, AfterContentChecked {
126119
}
127120

128121
public addUser() {
129-
this.form.get('users').push(this.fb.group({
122+
this.form.controls.users.push(this.fb.group({
130123
username: ['', Validators.required],
131124
password: ['', Validators.required],
132125
can_immediately_watch_movies: [false],
@@ -136,7 +129,7 @@ export class SettingsComponent implements OnInit, AfterContentChecked {
136129

137130
public saveUser(index: number) {
138131

139-
const userControl = this.form.get('users').controls[index];
132+
const userControl = this.form.controls.users.controls[index];
140133
if (!userControl.valid) {
141134
this.toastr.error('Please supply all required fields for this user');
142135
return;
@@ -157,7 +150,7 @@ export class SettingsComponent implements OnInit, AfterContentChecked {
157150
this.apiService.createUser(userControl.value.username, userControl.value.password).subscribe(
158151
(data) => {
159152
this.toastr.success(`Added ${userControl.value.username}`);
160-
this.form.get('users').at(index).addControl('id', new UntypedFormControl(data.id));
153+
this.form.controls.users.at(index).addControl('id', new FormControl(data.id));
161154
},
162155
(error) => {
163156
this.toastr.error(`An unknown error occurred adding user ${userControl.value.username}`);
@@ -168,11 +161,11 @@ export class SettingsComponent implements OnInit, AfterContentChecked {
168161
}
169162

170163
public removeUser(index: number) {
171-
const userControl = this.form.get('users').controls[index];
164+
const userControl = this.form.controls.users.controls[index];
172165
this.apiService.deleteUser(userControl.value.id).subscribe(
173166
(data) => {
174167
this.toastr.success(`Successfully deleted ${userControl.value.username}`);
175-
this.form.get('users').removeAt(index);
168+
this.form.controls.users.removeAt(index);
176169
},
177170
(error) => {
178171
this.toastr.error(`An unknown error occurred deleting user ${userControl.value.username}`);
@@ -182,7 +175,7 @@ export class SettingsComponent implements OnInit, AfterContentChecked {
182175
}
183176

184177
public canDeleteUser(index: number) {
185-
const userControl = this.form.get('users').controls[index];
178+
const userControl = this.form.controls.users.controls[index];
186179
return userControl.get('id') && this.apiService.user.id !== userControl.get('id').value;
187180
}
188181

@@ -375,7 +368,7 @@ export class SettingsComponent implements OnInit, AfterContentChecked {
375368
}
376369

377370
protected _verifyJackettIndexers() {
378-
this.isVeryingJackettIndexers = true;
371+
this.isVerifyingJackettIndexers = true;
379372
this.apiService.verifyJackettIndexers().subscribe(
380373
(data: any[]) => {
381374
const failedIndexers = data.filter((indexer: any) => {
@@ -388,11 +381,11 @@ export class SettingsComponent implements OnInit, AfterContentChecked {
388381
} else {
389382
this.toastr.success('All indexers were successful');
390383
}
391-
this.isVeryingJackettIndexers = false;
384+
this.isVerifyingJackettIndexers = false;
392385
},
393386
(error) => {
394387
this.toastr.error('An unknown error occurred verifying jackett indexers');
395-
this.isVeryingJackettIndexers = false;
388+
this.isVerifyingJackettIndexers = false;
396389
},
397390
);
398391
}

0 commit comments

Comments
 (0)