Skip to content

Commit

Permalink
Migrated to signal queries
Browse files Browse the repository at this point in the history
  • Loading branch information
emonney committed Dec 1, 2024
1 parent bfd6741 commit a084d63
Show file tree
Hide file tree
Showing 13 changed files with 134 additions and 148 deletions.
4 changes: 2 additions & 2 deletions quickapp.client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"@angular/localize": "^19.0.1",
"@types/jasmine": "~5.1.0",
"@types/node": "^22.10.1",
"angular-eslint": "19.0.0-alpha.4",
"angular-eslint": "19.0.0",
"eslint": "^9.15.0",
"jasmine-core": "~5.4.0",
"karma": "~6.4.0",
Expand All @@ -73,6 +73,6 @@
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0",
"typescript": "~5.6.2",
"typescript-eslint": "8.15.0"
"typescript-eslint": "8.16.0"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// (c) 2024 www.ebenmonney.com/mit-license
// ---------------------------------------

import { Component, OnInit, OnDestroy, TemplateRef, ViewChild, inject, input } from '@angular/core';
import { Component, OnInit, OnDestroy, TemplateRef, inject, input, viewChild } from '@angular/core';
import { NgClass } from '@angular/common';
import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap';
import { Subscription } from 'rxjs';
Expand Down Expand Up @@ -41,38 +41,31 @@ export class NotificationsViewerComponent implements OnInit, OnDestroy {
readonly isViewOnly = input(false);
readonly verticalScrollbar = input(false);

readonly statusHeaderTemplate = viewChild.required<TemplateRef<unknown>>('statusHeaderTemplate');

@ViewChild('statusHeaderTemplate', { static: true })
statusHeaderTemplate!: TemplateRef<unknown>;
readonly statusTemplate = viewChild.required<TemplateRef<unknown>>('statusTemplate');

@ViewChild('statusTemplate', { static: true })
statusTemplate!: TemplateRef<unknown>;
readonly dateTemplate = viewChild.required<TemplateRef<unknown>>('dateTemplate');

@ViewChild('dateTemplate', { static: true })
dateTemplate!: TemplateRef<unknown>;
readonly contentHeaderTemplate = viewChild.required<TemplateRef<unknown>>('contentHeaderTemplate');

@ViewChild('contentHeaderTemplate', { static: true })
contentHeaderTemplate!: TemplateRef<unknown>;
readonly contenBodytTemplate = viewChild.required<TemplateRef<unknown>>('contenBodytTemplate');

@ViewChild('contenBodytTemplate', { static: true })
contenBodytTemplate!: TemplateRef<unknown>;

@ViewChild('actionsTemplate', { static: true })
actionsTemplate!: TemplateRef<unknown>;
readonly actionsTemplate = viewChild.required<TemplateRef<unknown>>('actionsTemplate');

ngOnInit() {
if (this.isViewOnly()) {
this.columns = [
{ prop: 'header', cellTemplate: this.contentHeaderTemplate, width: 200, resizeable: false, sortable: false, draggable: false },
{ prop: 'header', cellTemplate: this.contentHeaderTemplate(), width: 200, resizeable: false, sortable: false, draggable: false },
];
} else {
const gT = (key: string) => this.translationService.getTranslation(key);

this.columns = [
{ prop: '', name: '', width: 10, headerTemplate: this.statusHeaderTemplate, cellTemplate: this.statusTemplate, resizeable: false, canAutoResize: false, sortable: false, draggable: false },
{ prop: 'date', name: gT('notifications.Date'), cellTemplate: this.dateTemplate, width: 30 },
{ prop: 'body', name: gT('notifications.Notification'), cellTemplate: this.contenBodytTemplate, width: 500 },
{ name: '', width: 80, cellTemplate: this.actionsTemplate, resizeable: false, canAutoResize: false, sortable: false, draggable: false }
{ prop: '', name: '', width: 10, headerTemplate: this.statusHeaderTemplate(), cellTemplate: this.statusTemplate(), resizeable: false, canAutoResize: false, sortable: false, draggable: false },
{ prop: 'date', name: gT('notifications.Date'), cellTemplate: this.dateTemplate(), width: 30 },
{ prop: 'body', name: gT('notifications.Notification'), cellTemplate: this.contenBodytTemplate(), width: 500 },
{ name: '', width: 80, cellTemplate: this.actionsTemplate(), resizeable: false, canAutoResize: false, sortable: false, draggable: false }
];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// (c) 2024 www.ebenmonney.com/mit-license
// ---------------------------------------

import { Component, OnInit, ViewChild, inject, output } from '@angular/core';
import { Component, OnInit, inject, output, viewChild } from '@angular/core';
import { NgClass } from '@angular/common';
import { HttpErrorResponse } from '@angular/common/http';
import { NgForm, FormsModule } from '@angular/forms';
Expand Down Expand Up @@ -44,8 +44,7 @@ export class RoleEditorComponent implements OnInit {
// Outupt to broadcast this instance so it can be accessible from within ng-bootstrap modal template
readonly afterOnInit = output<RoleEditorComponent>();

@ViewChild('f')
private form!: NgForm;
readonly form = viewChild.required<NgForm>('f');

ngOnInit() {
this.afterOnInit.emit(this);
Expand Down Expand Up @@ -162,7 +161,7 @@ export class RoleEditorComponent implements OnInit {

resetForm(replace = false) {
if (!replace) {
this.form.reset();
this.form().reset();
} else {
this.formResetToggle = false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// (c) 2024 www.ebenmonney.com/mit-license
// ---------------------------------------

import { Component, OnInit, TemplateRef, ViewChild, inject } from '@angular/core';
import { Component, OnInit, TemplateRef, inject, viewChild } from '@angular/core';
import { TranslateModule } from '@ngx-translate/core';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { TableColumn, NgxDatatableModule } from '@siemens/ngx-datatable';
Expand All @@ -23,10 +23,10 @@ interface RoleIndex extends Role {
}

@Component({
selector: 'app-roles-management',
templateUrl: './roles-management.component.html',
styleUrl: './roles-management.component.scss',
imports: [SearchBoxComponent, NgxDatatableModule, RoleEditorComponent, TranslateModule]
selector: 'app-roles-management',
templateUrl: './roles-management.component.html',
styleUrl: './roles-management.component.scss',
imports: [SearchBoxComponent, NgxDatatableModule, RoleEditorComponent, TranslateModule]
})
export class RolesManagementComponent implements OnInit {
private alertService = inject(AlertService);
Expand All @@ -43,27 +43,23 @@ export class RolesManagementComponent implements OnInit {
editingRoleName: { name: string } | null = null;
loadingIndicator = false;

readonly indexTemplate = viewChild.required<TemplateRef<unknown>>('indexTemplate');

@ViewChild('indexTemplate', { static: true })
indexTemplate!: TemplateRef<unknown>;
readonly actionsTemplate = viewChild.required<TemplateRef<unknown>>('actionsTemplate');

@ViewChild('actionsTemplate', { static: true })
actionsTemplate!: TemplateRef<unknown>;

@ViewChild('editorModal', { static: true })
editorModalTemplate!: TemplateRef<unknown>;
readonly editorModalTemplate = viewChild.required<TemplateRef<unknown>>('editorModal');

roleEditor: RoleEditorComponent | null = null;

ngOnInit() {
const gT = (key: string) => this.translationService.getTranslation(key);

this.columns = [
{ prop: 'index', name: '#', width: 50, cellTemplate: this.indexTemplate, canAutoResize: false },
{ prop: 'index', name: '#', width: 50, cellTemplate: this.indexTemplate(), canAutoResize: false },
{ prop: 'name', name: gT('roles.management.Name'), width: 180 },
{ prop: 'description', name: gT('roles.management.Description'), width: 320 },
{ prop: 'usersCount', name: gT('roles.management.Users'), width: 50 },
{ name: '', width: 160, cellTemplate: this.actionsTemplate, resizeable: false, canAutoResize: false, sortable: false, draggable: false }
{ name: '', width: 160, cellTemplate: this.actionsTemplate(), resizeable: false, canAutoResize: false, sortable: false, draggable: false }
];

this.loadData();
Expand Down Expand Up @@ -166,7 +162,7 @@ export class RolesManagementComponent implements OnInit {
}

openRoleEditor() {
const modalRef = this.modalService.open(this.editorModalTemplate, {
const modalRef = this.modalService.open(this.editorModalTemplate(), {
size: 'lg',
backdrop: 'static'
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// (c) 2024 www.ebenmonney.com/mit-license
// ---------------------------------------

import { Component, ViewChild, ElementRef, HostListener, input, output } from '@angular/core';
import { Component, ElementRef, HostListener, input, output, viewChild } from '@angular/core';
import { FormsModule } from '@angular/forms';

@Component({
Expand All @@ -18,16 +18,17 @@ export class SearchBoxComponent {

readonly searchChange = output<string>();

@ViewChild('searchInput')
searchInput!: ElementRef;
readonly searchInput = viewChild.required<ElementRef>('searchInput');

onValueChange(value: string) {
setTimeout(() => this.searchChange.emit(value));
}

@HostListener('keydown.escape')
clear() {
this.searchInput.nativeElement.value = '';
this.onValueChange(this.searchInput.nativeElement.value);
const searchInput = this.searchInput();

searchInput.nativeElement.value = '';
this.onValueChange(searchInput.nativeElement.value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// (c) 2024 www.ebenmonney.com/mit-license
// ---------------------------------------

import { Component, OnInit, OnDestroy, ViewChild, inject } from '@angular/core';
import { Component, OnInit, OnDestroy, inject, viewChild } from '@angular/core';
import { AlertService, DialogType, MessageSeverity } from '../../services/alert.service';
import { BaseChartDirective } from 'ng2-charts';
import { ChartEvent, ChartType } from 'chart.js';
Expand All @@ -14,10 +14,10 @@ import { NgbDropdown, NgbDropdownToggle, NgbDropdownMenu, NgbDropdownButtonItem,
interface ChartEventArgs { event: ChartEvent; active: object[] }

@Component({
selector: 'app-statistics-demo',
templateUrl: './statistics-demo.component.html',
styleUrl: './statistics-demo.component.scss',
imports: [BaseChartDirective, NgbDropdown, NgbDropdownToggle, NgbDropdownMenu, NgbDropdownButtonItem, NgbDropdownItem]
selector: 'app-statistics-demo',
templateUrl: './statistics-demo.component.html',
styleUrl: './statistics-demo.component.scss',
imports: [BaseChartDirective, NgbDropdown, NgbDropdownToggle, NgbDropdownMenu, NgbDropdownButtonItem, NgbDropdownItem]
})
export class StatisticsDemoComponent implements OnInit, OnDestroy {
private alertService = inject(AlertService);
Expand Down Expand Up @@ -45,8 +45,7 @@ export class StatisticsDemoComponent implements OnInit, OnDestroy {

timerReference: ReturnType<typeof setInterval> | undefined;

@ViewChild(BaseChartDirective)
chart!: BaseChartDirective;
readonly chart = viewChild.required(BaseChartDirective);

ngOnInit() {
this.refreshChartOptions();
Expand Down Expand Up @@ -91,7 +90,7 @@ export class StatisticsDemoComponent implements OnInit, OnDestroy {
}
}

this.chart.update();
this.chart().update();
}

changeChartType(type: ChartType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@

<ng-template #nameTemplate let-row="row" let-value="value">
@if (editing[row.$$index + '-name']) {
<input class="inline-editor" appAutofocus (blur)="updateValue($event, 'name', value, row)" type="text" [value]="value" />
<input class="inline-editor" appAutofocus type="text" [value]="value"
(blur)="updateValue($event, 'name', row)"
(keydown.enter)="updateValue($event, 'name', row)"
(keydown.escape)="editing[row.$$index + '-name'] = false"/>
}
@else {
<span class="inline-label" [class.completed]="row.completed" attr.title="Double click to edit - {{value}}"
Expand All @@ -54,7 +57,10 @@

<ng-template #descriptionTemplate let-row="row" let-value="value">
@if (editing[row.$$index + '-description']) {
<input class="inline-editor" appAutofocus (blur)="updateValue($event, 'description', value, row)" type="text" [value]="value" />
<input class="inline-editor" appAutofocus type="text" [value]="value"
(blur)="updateValue($event, 'description', row)"
(keydown.enter)="updateValue($event, 'description', row)"
(keydown.escape)="editing[row.$$index + '-description'] = false"/>
}
@else {
<span class="inline-label" [class.completed]="row.completed" attr.title="Double click to edit - {{value}}"
Expand Down
36 changes: 15 additions & 21 deletions quickapp.client/src/app/components/controls/todo-demo.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// (c) 2024 www.ebenmonney.com/mit-license
// ---------------------------------------

import { Component, OnInit, OnDestroy, TemplateRef, ViewChild, inject, input } from '@angular/core';
import { Component, OnInit, OnDestroy, TemplateRef, inject, input, viewChild } from '@angular/core';
import { NgClass } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { TranslateModule } from '@ngx-translate/core';
Expand Down Expand Up @@ -76,23 +76,17 @@ export class TodoDemoComponent implements OnInit, OnDestroy {

readonly verticalScrollbar = input(false);

@ViewChild('statusHeaderTemplate', { static: true })
statusHeaderTemplate!: TemplateRef<unknown>;
readonly statusHeaderTemplate = viewChild.required<TemplateRef<unknown>>('statusHeaderTemplate');

@ViewChild('statusTemplate', { static: true })
statusTemplate!: TemplateRef<unknown>;
readonly statusTemplate = viewChild.required<TemplateRef<unknown>>('statusTemplate');

@ViewChild('nameTemplate', { static: true })
nameTemplate!: TemplateRef<unknown>;
readonly nameTemplate = viewChild.required<TemplateRef<unknown>>('nameTemplate');

@ViewChild('descriptionTemplate', { static: true })
descriptionTemplate!: TemplateRef<unknown>;
readonly descriptionTemplate = viewChild.required<TemplateRef<unknown>>('descriptionTemplate');

@ViewChild('actionsTemplate', { static: true })
actionsTemplate!: TemplateRef<unknown>;
readonly actionsTemplate = viewChild.required<TemplateRef<unknown>>('actionsTemplate');

@ViewChild('editorModal', { static: true })
editorModalTemplate!: TemplateRef<unknown>;
readonly editorModalTemplate = viewChild.required<TemplateRef<unknown>>('editorModal');

ngOnInit() {
this.loadingIndicator = true;
Expand All @@ -113,8 +107,8 @@ export class TodoDemoComponent implements OnInit, OnDestroy {
prop: 'completed',
name: '',
width: 30,
headerTemplate: this.statusHeaderTemplate,
cellTemplate: this.statusTemplate,
headerTemplate: this.statusHeaderTemplate(),
cellTemplate: this.statusTemplate(),
resizeable: false,
canAutoResize: false,
sortable: false,
Expand All @@ -124,18 +118,18 @@ export class TodoDemoComponent implements OnInit, OnDestroy {
prop: 'name',
name: gT('todoDemo.management.Task'),
width: 100,
cellTemplate: this.nameTemplate
cellTemplate: this.nameTemplate()
},
{
prop: 'description',
name: gT('todoDemo.management.Description'),
width: 300,
cellTemplate: this.descriptionTemplate
cellTemplate: this.descriptionTemplate()
},
{
name: '',
width: 80,
cellTemplate: this.actionsTemplate,
cellTemplate: this.actionsTemplate(),
resizeable: false,
canAutoResize: false,
sortable: false,
Expand Down Expand Up @@ -213,7 +207,7 @@ export class TodoDemoComponent implements OnInit, OnDestroy {
this.formResetToggle = true;

this.taskEdit = {};
this.modalService.open(this.editorModalTemplate);
this.modalService.open(this.editorModalTemplate());
});
}

Expand All @@ -227,9 +221,9 @@ export class TodoDemoComponent implements OnInit, OnDestroy {
return true;
}

updateValue(event: Event, cell: 'name' | 'description', cellValue: string, row: Todo) {
updateValue(event: Event, cell: 'name' | 'description', row: Todo) {
this.editing[row.$$index + '-' + cell] = false;
this.rows[row.$$index as number][cell] = (event.target as HTMLInputElement).value;
row[cell] = (event.target as HTMLInputElement).value;
this.rows = [...this.rows];

this.saveToDisk();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,7 @@
<div>
@if (formResetToggle) {
<form [attr.autocomplete]="isGeneralEditor ? 'NaN' : null" name="userInfoForm" #f="ngForm" novalidate
(ngSubmit)="f.form.valid ? save() :
(!userName.valid && showErrorAlert('User name is required', 'Please enter a user name (minimum of 2 and maximum of 200 characters)'));
(userPassword && !userPassword.valid && showErrorAlert('Password is required', 'Please enter the current password'));
(email.errors?.['required'] && showErrorAlert('Email is required', 'Please enter an email address (maximum of 200 characters)'));
(email.errors?.['pattern'] && showErrorAlert('Invalid Email', 'Please enter a valid email address'));
(isChangePassword && isEditingSelf && !currentPassword.valid && showErrorAlert('Current password is required', 'Please enter the current password'));
((isChangePassword || isNewUser) && !newPassword.valid && showErrorAlert('New password is required', 'Please enter the new password (minimum of 6 characters)'));
((isChangePassword || isNewUser) && newPassword.valid && confirmPassword.errors?.['required'] && showErrorAlert('Confirmation password is required', 'Please enter the confirmation password'));
((isChangePassword || isNewUser) && newPassword.valid && confirmPassword.errors?.['validateEqual'] && showErrorAlert('Passwword mismatch', 'New password and confirmation password do not match'));
(canAssignRoles && !roles.valid && showErrorAlert('Roles is required', 'Please select a minimum of 1 role'));">
(ngSubmit)="f.form.valid ? save() : showValidationAlerts()">
<div class="row">
<label [class.col-lg-3]="isViewOnly()" [class.col-lg-2]="!isViewOnly()" class="col-form-label" for="jobTitle-{{uniqueId}}">
{{'users.editor.JobTitle' | translate}}
Expand Down Expand Up @@ -258,9 +242,9 @@
</ng-template>
</ng-select>
@if (showValidationErrors && f.submitted && !roles.valid) {
<span class="invalid-feedback">
{{'users.editor.RoleRequired' | translate}}
</span>
<span class="invalid-feedback">
{{'users.editor.RoleRequired' | translate}}
</span>
}
</div>
}
Expand Down
Loading

0 comments on commit a084d63

Please sign in to comment.