Skip to content

Commit a1f5329

Browse files
docs: migrate to inject function
1 parent 9cfc91e commit a1f5329

24 files changed

+158
-132
lines changed

projects/example-app/src/app/auth/components/__snapshots__/login-form.component.spec.ts.snap

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
exports[`Login Page should compile 1`] = `
44
<bc-login-form
5+
errorMessage="null"
56
form={[Function FormGroup]}
67
submitted={[Function EventEmitter_]}
78
>
@@ -40,6 +41,7 @@ exports[`Login Page should compile 1`] = `
4041
aria-invalid="false"
4142
aria-required="false"
4243
class="mat-mdc-input-element mat-mdc-form-field-input-control mdc-text-field__input ng-untouched ng-pristine ng-valid cdk-text-field-autofill-monitored"
44+
data-testid="username"
4345
formcontrolname="username"
4446
matinput=""
4547
placeholder="Username"
@@ -86,6 +88,7 @@ exports[`Login Page should compile 1`] = `
8688
aria-invalid="false"
8789
aria-required="false"
8890
class="mat-mdc-input-element mat-mdc-form-field-input-control mdc-text-field__input ng-untouched ng-pristine ng-valid cdk-text-field-autofill-monitored"
91+
data-testid="password"
8992
formcontrolname="password"
9093
matinput=""
9194
placeholder="Password"
@@ -146,6 +149,7 @@ exports[`Login Page should compile 1`] = `
146149

147150
exports[`Login Page should disable the form if pending 1`] = `
148151
<bc-login-form
152+
errorMessage="null"
149153
form={[Function FormGroup]}
150154
submitted={[Function EventEmitter_]}
151155
>
@@ -181,6 +185,7 @@ exports[`Login Page should disable the form if pending 1`] = `
181185
aria-invalid="false"
182186
aria-required="false"
183187
class="mat-mdc-input-element mat-mdc-form-field-input-control mdc-text-field__input ng-untouched ng-pristine cdk-text-field-autofill-monitored"
188+
data-testid="username"
184189
disabled=""
185190
formcontrolname="username"
186191
matinput=""
@@ -225,6 +230,7 @@ exports[`Login Page should disable the form if pending 1`] = `
225230
aria-invalid="false"
226231
aria-required="false"
227232
class="mat-mdc-input-element mat-mdc-form-field-input-control mdc-text-field__input ng-untouched ng-pristine cdk-text-field-autofill-monitored"
233+
data-testid="password"
228234
disabled=""
229235
formcontrolname="password"
230236
matinput=""
@@ -325,6 +331,7 @@ exports[`Login Page should display an error message if provided 1`] = `
325331
aria-invalid="false"
326332
aria-required="false"
327333
class="mat-mdc-input-element mat-mdc-form-field-input-control mdc-text-field__input ng-untouched ng-pristine ng-valid cdk-text-field-autofill-monitored"
334+
data-testid="username"
328335
formcontrolname="username"
329336
matinput=""
330337
placeholder="Username"
@@ -371,6 +378,7 @@ exports[`Login Page should display an error message if provided 1`] = `
371378
aria-invalid="false"
372379
aria-required="false"
373380
class="mat-mdc-input-element mat-mdc-form-field-input-control mdc-text-field__input ng-untouched ng-pristine ng-valid cdk-text-field-autofill-monitored"
381+
data-testid="password"
374382
formcontrolname="password"
375383
matinput=""
376384
placeholder="Password"

projects/example-app/src/app/auth/components/login-form.component.spec.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { TestBed, ComponentFixture } from '@angular/core/testing';
22
import { NO_ERRORS_SCHEMA } from '@angular/core';
33
import { LoginFormComponent } from '@example-app/auth/components';
44
import { provideNoopAnimations } from '@angular/platform-browser/animations';
5+
import { By } from '@angular/platform-browser';
56

67
describe('Login Page', () => {
78
let fixture: ComponentFixture<LoginFormComponent>;
@@ -57,7 +58,19 @@ describe('Login Page', () => {
5758
username: 'user',
5859
password: 'pass',
5960
};
60-
instance.form.setValue(credentials);
61+
62+
const inpUsername: HTMLInputElement = fixture.debugElement.query(
63+
By.css('input[data-testid=username]')
64+
).nativeElement;
65+
const inpPassword: HTMLInputElement = fixture.debugElement.query(
66+
By.css('input[data-testid=password]')
67+
).nativeElement;
68+
69+
fixture.detectChanges();
70+
inpUsername.value = credentials.username;
71+
inpUsername.dispatchEvent(new Event('input'));
72+
inpPassword.value = credentials.password;
73+
inpPassword.dispatchEvent(new Event('input'));
6174

6275
jest.spyOn(instance.submitted, 'emit');
6376
instance.submit();

projects/example-app/src/app/auth/components/login-form.component.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { MaterialModule } from '@example-app/material';
1919
matInput
2020
placeholder="Username"
2121
formControlName="username"
22+
data-testid="username"
2223
/>
2324
</mat-form-field>
2425
</p>
@@ -30,6 +31,7 @@ import { MaterialModule } from '@example-app/material';
3031
matInput
3132
placeholder="Password"
3233
formControlName="password"
34+
data-testid="password"
3335
/>
3436
</mat-form-field>
3537
</p>
@@ -94,11 +96,11 @@ export class LoginFormComponent {
9496
}
9597
}
9698

97-
@Input() errorMessage!: string | null;
99+
@Input() errorMessage: string | null = null;
98100

99101
@Output() submitted = new EventEmitter<Credentials>();
100102

101-
form: FormGroup = new FormGroup({
103+
protected readonly form: FormGroup = new FormGroup({
102104
username: new FormControl('ngrx'),
103105
password: new FormControl(''),
104106
});

projects/example-app/src/app/auth/containers/__snapshots__/login-page.component.spec.ts.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ exports[`Login Page should compile 1`] = `
4242
aria-invalid="false"
4343
aria-required="false"
4444
class="mat-mdc-input-element mat-mdc-form-field-input-control mdc-text-field__input ng-untouched ng-pristine ng-valid cdk-text-field-autofill-monitored"
45+
data-testid="username"
4546
formcontrolname="username"
4647
matinput=""
4748
placeholder="Username"
@@ -88,6 +89,7 @@ exports[`Login Page should compile 1`] = `
8889
aria-invalid="false"
8990
aria-required="false"
9091
class="mat-mdc-input-element mat-mdc-form-field-input-control mdc-text-field__input ng-untouched ng-pristine ng-valid cdk-text-field-autofill-monitored"
92+
data-testid="password"
9193
formcontrolname="password"
9294
matinput=""
9395
placeholder="Password"

projects/example-app/src/app/auth/containers/login-page.component.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component } from '@angular/core';
1+
import { Component, inject } from '@angular/core';
22
import { Store } from '@ngrx/store';
33
import { Credentials } from '@example-app/auth/models';
44
import * as fromAuth from '@example-app/auth/reducers';
@@ -21,10 +21,12 @@ import { AsyncPipe } from '@angular/common';
2121
styles: [],
2222
})
2323
export class LoginPageComponent {
24-
pending$ = this.store.select(fromAuth.selectLoginPagePending);
25-
error$ = this.store.select(fromAuth.selectLoginPageError);
24+
private store = inject(Store);
2625

27-
constructor(private store: Store) {}
26+
protected readonly pending$ = this.store.select(
27+
fromAuth.selectLoginPagePending
28+
);
29+
protected readonly error$ = this.store.select(fromAuth.selectLoginPageError);
2830

2931
onSubmit(credentials: Credentials) {
3032
this.store.dispatch(LoginPageActions.login({ credentials }));

projects/example-app/src/app/auth/effects/auth.effects.ts

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Injectable } from '@angular/core';
1+
import { Injectable, inject } from '@angular/core';
22
import { MatDialog } from '@angular/material/dialog';
33
import { Router } from '@angular/router';
44
import { Actions, ofType, createEffect } from '@ngrx/effects';
@@ -14,7 +14,12 @@ import { UserActions } from '@example-app/core/actions/user.actions';
1414

1515
@Injectable()
1616
export class AuthEffects {
17-
login$ = createEffect(() =>
17+
private readonly actions$ = inject(Actions);
18+
private readonly authService = inject(AuthService);
19+
private readonly router = inject(Router);
20+
private readonly dialog = inject(MatDialog);
21+
22+
readonly login$ = createEffect(() =>
1823
this.actions$.pipe(
1924
ofType(LoginPageActions.login),
2025
map((action) => action.credentials),
@@ -27,7 +32,7 @@ export class AuthEffects {
2732
)
2833
);
2934

30-
loginSuccess$ = createEffect(
35+
readonly loginSuccess$ = createEffect(
3136
() =>
3237
this.actions$.pipe(
3338
ofType(AuthApiActions.loginSuccess),
@@ -36,7 +41,7 @@ export class AuthEffects {
3641
{ dispatch: false }
3742
);
3843

39-
loginRedirect$ = createEffect(
44+
readonly loginRedirect$ = createEffect(
4045
() =>
4146
this.actions$.pipe(
4247
ofType(AuthApiActions.loginRedirect, AuthActions.logout),
@@ -47,7 +52,7 @@ export class AuthEffects {
4752
{ dispatch: false }
4853
);
4954

50-
logoutConfirmation$ = createEffect(() =>
55+
readonly logoutConfirmation$ = createEffect(() =>
5156
this.actions$.pipe(
5257
ofType(AuthActions.logoutConfirmation),
5358
exhaustMap(() => {
@@ -65,17 +70,10 @@ export class AuthEffects {
6570
)
6671
);
6772

68-
logoutIdleUser$ = createEffect(() =>
73+
readonly logoutIdleUser$ = createEffect(() =>
6974
this.actions$.pipe(
7075
ofType(UserActions.idleTimeout),
7176
map(() => AuthActions.logout())
7277
)
7378
);
74-
75-
constructor(
76-
private actions$: Actions,
77-
private authService: AuthService,
78-
private router: Router,
79-
private dialog: MatDialog
80-
) {}
8179
}

projects/example-app/src/app/auth/services/auth.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { Credentials, User } from '@example-app/auth/models';
77
providedIn: 'root',
88
})
99
export class AuthService {
10-
login({ username, password }: Credentials): Observable<User> {
10+
login({ username }: Credentials): Observable<User> {
1111
/**
1212
* Simulate a failed login to display the error
1313
* message for the login form.

projects/example-app/src/app/books/components/book-authors.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ import { AddCommasPipe } from '@example-app/shared/pipes/add-commas.pipe';
2323
],
2424
})
2525
export class BookAuthorsComponent {
26-
@Input() book!: Book;
26+
@Input() book: Book | undefined = undefined;
2727

2828
get authors() {
29-
return this.book.volumeInfo.authors;
29+
return this.book?.volumeInfo.authors || [];
3030
}
3131
}

projects/example-app/src/app/books/components/book-detail.component.ts

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -78,35 +78,31 @@ export class BookDetailComponent {
7878
*
7979
* More on 'smart' and 'presentational' components: https://gist.github.com/btroncone/a6e4347326749f938510#utilizing-container-components
8080
*/
81-
@Input() book!: Book;
82-
@Input() inCollection!: boolean;
81+
@Input() book: Book | undefined = undefined;
82+
@Input() inCollection = false;
8383
@Output() add = new EventEmitter<Book>();
8484
@Output() remove = new EventEmitter<Book>();
8585

8686
/**
8787
* Tip: Utilize getters to keep templates clean
8888
*/
8989
get id() {
90-
return this.book.id;
90+
return this.book?.id;
9191
}
9292

9393
get title() {
94-
return this.book.volumeInfo.title;
94+
return this.book?.volumeInfo.title;
9595
}
9696

9797
get subtitle() {
98-
return this.book.volumeInfo.subtitle;
98+
return this.book?.volumeInfo.subtitle;
9999
}
100100

101101
get description() {
102-
return this.book.volumeInfo.description;
102+
return this.book?.volumeInfo.description;
103103
}
104104

105105
get thumbnail() {
106-
return (
107-
this.book.volumeInfo.imageLinks &&
108-
this.book.volumeInfo.imageLinks.smallThumbnail &&
109-
this.book.volumeInfo.imageLinks.smallThumbnail.replace('http:', '')
110-
);
106+
return this.book?.volumeInfo.imageLinks.smallThumbnail.replace('http:', '');
111107
}
112108
}

projects/example-app/src/app/books/components/book-preview-list.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ import { BookPreviewComponent } from './book-preview.component';
2323
],
2424
})
2525
export class BookPreviewListComponent {
26-
@Input() books!: Book[];
26+
@Input() books = new Array<Book>();
2727
}

projects/example-app/src/app/books/components/book-preview.component.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ import { BookAuthorsComponent } from './book-authors.component';
1919
}
2020
<mat-card-title>{{ title | bcEllipsis : 35 }}</mat-card-title>
2121
@if (subtitle) {
22-
<mat-card-subtitle>{{
23-
subtitle | bcEllipsis : 40
24-
}}</mat-card-subtitle>
22+
<mat-card-subtitle
23+
>{{ subtitle | bcEllipsis : 40 }}
24+
</mat-card-subtitle>
2525
}
2626
</mat-card-title-group>
2727
<mat-card-content>
@@ -85,26 +85,26 @@ import { BookAuthorsComponent } from './book-authors.component';
8585
],
8686
})
8787
export class BookPreviewComponent {
88-
@Input() book!: Book;
88+
@Input() book: Book | undefined = undefined;
8989

9090
get id() {
91-
return this.book.id;
91+
return this.book?.id;
9292
}
9393

9494
get title() {
95-
return this.book.volumeInfo.title;
95+
return this.book?.volumeInfo.title || '';
9696
}
9797

9898
get subtitle() {
99-
return this.book.volumeInfo.subtitle;
99+
return this.book?.volumeInfo.subtitle;
100100
}
101101

102102
get description() {
103-
return this.book.volumeInfo.description;
103+
return this.book?.volumeInfo.description;
104104
}
105105

106106
get thumbnail(): string | boolean {
107-
if (this.book.volumeInfo.imageLinks) {
107+
if (this.book?.volumeInfo.imageLinks) {
108108
return this.book.volumeInfo.imageLinks.smallThumbnail.replace(
109109
'http:',
110110
''

projects/example-app/src/app/books/containers/__snapshots__/view-book-page.component.spec.ts.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
exports[`View Book Page should compile 1`] = `
44
<bc-view-book-page
55
actionsSubscription={[Function SafeSubscriber]}
6+
store={[Function _MockStore]}
67
>
78
<bc-selected-book-page>
89
<bc-book-detail>

projects/example-app/src/app/books/containers/collection-page.component.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
1+
import {
2+
ChangeDetectionStrategy,
3+
Component,
4+
inject,
5+
OnInit,
6+
} from '@angular/core';
27

38
import { Store } from '@ngrx/store';
49
import { Observable } from 'rxjs';
@@ -39,11 +44,9 @@ import { AsyncPipe } from '@angular/common';
3944
],
4045
})
4146
export class CollectionPageComponent implements OnInit {
42-
books$: Observable<Book[]>;
47+
private readonly store = inject(Store);
4348

44-
constructor(private store: Store) {
45-
this.books$ = store.select(fromBooks.selectBookCollection);
46-
}
49+
protected books$ = this.store.select(fromBooks.selectBookCollection);
4750

4851
ngOnInit() {
4952
this.store.dispatch(CollectionPageActions.enter());

0 commit comments

Comments
 (0)