Poorly designed starting point component: almost done!

This commit is contained in:
Z. Charles Dziura 2025-07-01 11:31:57 -04:00
parent 171377d0b5
commit d13bfbbd25
9 changed files with 115 additions and 65 deletions

29
package-lock.json generated
View file

@ -15,8 +15,6 @@
"@angular/platform-browser": "^20.0.0", "@angular/platform-browser": "^20.0.0",
"@angular/router": "^20.0.0", "@angular/router": "^20.0.0",
"@ngrx/signals": "^19.2.1", "@ngrx/signals": "^19.2.1",
"@ngrx/store": "^19.2.1",
"@ngrx/store-devtools": "^19.2.1",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
"tslib": "^2.3.0" "tslib": "^2.3.0"
}, },
@ -2246,33 +2244,6 @@
} }
} }
}, },
"node_modules/@ngrx/store": {
"version": "19.2.1",
"resolved": "https://registry.npmjs.org/@ngrx/store/-/store-19.2.1.tgz",
"integrity": "sha512-c5vQId7YoAhM0y4HASrz9mtLju+28vJspd6OBlhPbBlSae8GN8m9S/oav+8LaSY19yh95cZ5B/nMcLNNWgL/jA==",
"license": "MIT",
"dependencies": {
"tslib": "^2.0.0"
},
"peerDependencies": {
"@angular/core": "^19.0.0",
"rxjs": "^6.5.3 || ^7.5.0"
}
},
"node_modules/@ngrx/store-devtools": {
"version": "19.2.1",
"resolved": "https://registry.npmjs.org/@ngrx/store-devtools/-/store-devtools-19.2.1.tgz",
"integrity": "sha512-gj1YO+4yl6D0l9vzLWdw07TQSu5UPKgsSLsNJfDLXraaLCUcB8voAp4J7zohN8qR5ixDuHeMoiSSVuklQ75u2w==",
"license": "MIT",
"dependencies": {
"tslib": "^2.0.0"
},
"peerDependencies": {
"@angular/core": "^19.0.0",
"@ngrx/store": "19.2.1",
"rxjs": "^6.5.3 || ^7.5.0"
}
},
"node_modules/@npmcli/agent": { "node_modules/@npmcli/agent": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-3.0.0.tgz", "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-3.0.0.tgz",

View file

@ -16,8 +16,6 @@
"@angular/platform-browser": "^20.0.0", "@angular/platform-browser": "^20.0.0",
"@angular/router": "^20.0.0", "@angular/router": "^20.0.0",
"@ngrx/signals": "^19.2.1", "@ngrx/signals": "^19.2.1",
"@ngrx/store": "^19.2.1",
"@ngrx/store-devtools": "^19.2.1",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
"tslib": "^2.3.0" "tslib": "^2.3.0"
}, },

View file

@ -1,21 +1,5 @@
import { import { ApplicationConfig, provideBrowserGlobalErrorListeners, provideZonelessChangeDetection } from '@angular/core';
ApplicationConfig,
isDevMode,
provideBrowserGlobalErrorListeners,
provideZonelessChangeDetection,
} from '@angular/core';
import { provideStoreDevtools } from '@ngrx/store-devtools';
export const appConfig: ApplicationConfig = { export const appConfig: ApplicationConfig = {
providers: [ providers: [provideBrowserGlobalErrorListeners(), provideZonelessChangeDetection()],
provideBrowserGlobalErrorListeners(),
provideZonelessChangeDetection(),
provideStoreDevtools({
maxAge: 25,
logOnly: !isDevMode(),
autoPause: true,
trace: false,
traceLimit: 75,
}),
],
}; };

View file

@ -1,5 +1,8 @@
<main> <main>
@switch (currentView()) { @switch (currentView()) {
@case ('startingPoint') {
<app-starting-point></app-starting-point>
}
@default { @default {
<app-starting-point></app-starting-point> <app-starting-point></app-starting-point>
} }

View file

@ -1,15 +1,24 @@
import { patchState, signalStore, withMethods, withState } from '@ngrx/signals'; import { patchState, signalStore, watchState, withHooks, withMethods, withState } from '@ngrx/signals';
import { AfterHighSchool, Gender } from './views/starting-point/starting-point.state';
export type AppState = { export type AppState = {
currentView: CurrentView; currentView: CurrentView;
name: string;
gender: Gender;
afterHighSchool: AfterHighSchool;
}; };
export enum CurrentView { export enum CurrentView {
StartingPoint = 'startingPoint', StartingPoint = 'startingPoint',
CollegeSelection = 'collegeSelection',
FirstJobHighSchoolDiploma = 'firstJobHighSchoolDiploma',
} }
const initialState: AppState = { const initialState: AppState = {
currentView: CurrentView.StartingPoint, currentView: CurrentView.StartingPoint,
name: '',
gender: Gender.Undefined,
afterHighSchool: AfterHighSchool.Undefined,
}; };
export const AppStateStore = signalStore( export const AppStateStore = signalStore(
@ -17,9 +26,31 @@ export const AppStateStore = signalStore(
withState(initialState), withState(initialState),
withMethods(store => ({ withMethods(store => ({
updateCurrentView: (currentView: CurrentView) => updateCurrentView: (currentView: CurrentView) =>
patchState(store, () => ({ patchState(store, state => ({
...store, ...state,
currentView, currentView,
})), })),
})) updateStartingPointState: ({
name,
gender,
afterHighSchool,
}: {
name: string;
gender: Gender;
afterHighSchool: AfterHighSchool;
}) =>
patchState(store, state => ({
...state,
name,
gender,
afterHighSchool,
})),
})),
withHooks({
onInit: store => {
watchState(store, ({ currentView }) => {
console.log(`Current View: ${currentView}`);
});
},
})
); );

View file

@ -1,7 +1,25 @@
<h1> <form [formGroup]="form" (submit)="onSubmit()">
Hello {{ name() }} <div class="name">
</h1> <label for="name">Your Name: </label>
<form [formGroup]="form"> <input id="name" type="text" formControlName="name">
<label for="name">Your Name: </label> </div>
<input id="name" type="text" formControlName="name"> <div class="gender">
<label for="gender">Your Gender:</label>
<select id="gender" formControlName="gender">
<option value="">--Please select a gender--</option>
@for (option of genders(); track $index) {
<option value="{{ option.value }}">{{ option.desc }}</option>
}
</select>
</div>
<div class="after-high-school">
<label for="after-high-school">What You're Doing After Graduation:</label>
<select id="after-high-school" formControlName="afterHighSchool">
<option value="">--Please select an option--</option>
@for (option of afterHighSchoolOptions(); track $index) {
<option value="{{ option.value }}">{{ option.desc }}</option>
}
</select>
</div>
<button type="submit">Next</button>
</form> </form>

View file

@ -1,4 +1,5 @@
import { patchState, signalStore, withMethods, withState } from '@ngrx/signals'; import { effect } from '@angular/core';
import { getState, patchState, signalStore, watchState, withHooks, withMethods, withState } from '@ngrx/signals';
export type StartingPointState = { export type StartingPointState = {
name: string; name: string;
@ -33,5 +34,12 @@ export const StartingPointStateStore = signalStore(
gender: gender ?? Gender.Undefined, gender: gender ?? Gender.Undefined,
afterHighSchool: afterHighSchool ?? AfterHighSchool.Undefined, afterHighSchool: afterHighSchool ?? AfterHighSchool.Undefined,
})), })),
})) })),
withHooks({
onInit: store => {
watchState(store, state => {
console.log(state);
});
},
})
); );

View file

@ -1,7 +1,9 @@
import { ChangeDetectionStrategy, Component, computed, inject, OnInit, Signal } from '@angular/core'; import { ChangeDetectionStrategy, Component, effect, inject, OnInit, signal } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
import { StartingPointState, StartingPointStateStore } from './starting-point.state'; import { StartingPointState, StartingPointStateStore } from './starting-point.state';
import { AppStateStore, CurrentView } from '../../app.state';
import { getState } from '@ngrx/signals';
export interface StartingPointForm { export interface StartingPointForm {
name: string; name: string;
@ -30,6 +32,7 @@ export enum AfterHighSchool {
providers: [StartingPointStateStore], providers: [StartingPointStateStore],
}) })
export class StartingPoint implements OnInit { export class StartingPoint implements OnInit {
private readonly globalState = inject(AppStateStore);
private readonly state = inject(StartingPointStateStore); private readonly state = inject(StartingPointStateStore);
readonly name = this.state.name; readonly name = this.state.name;
@ -40,6 +43,28 @@ export class StartingPoint implements OnInit {
afterHighSchool: [AfterHighSchool.Undefined, Validators.required], afterHighSchool: [AfterHighSchool.Undefined, Validators.required],
}); });
readonly genders = signal<Array<{ desc: string; value: Gender }>>([
{
desc: 'Female',
value: Gender.Female,
},
{
desc: 'Male',
value: Gender.Male,
},
]).asReadonly();
readonly afterHighSchoolOptions = signal<Array<{ desc: string; value: AfterHighSchool }>>([
{
desc: 'Go to College',
value: AfterHighSchool.College,
},
{
desc: 'Enter the Workforce',
value: AfterHighSchool.Workforce,
},
]).asReadonly();
constructor() { constructor() {
this.form.valueChanges this.form.valueChanges
.pipe(takeUntilDestroyed()) .pipe(takeUntilDestroyed())
@ -49,4 +74,19 @@ export class StartingPoint implements OnInit {
} }
ngOnInit(): void {} ngOnInit(): void {}
onSubmit() {
const startingPointState = getState(this.state);
this.globalState.updateStartingPointState(startingPointState);
switch (startingPointState.afterHighSchool) {
case AfterHighSchool.College:
this.globalState.updateCurrentView(CurrentView.CollegeSelection);
break;
case AfterHighSchool.Workforce:
this.globalState.updateCurrentView(CurrentView.FirstJobHighSchoolDiploma);
break;
}
}
} }

View file

@ -26,9 +26,6 @@
"references": [ "references": [
{ {
"path": "./tsconfig.app.json" "path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.spec.json"
} }
] ]
} }