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/router": "^20.0.0",
"@ngrx/signals": "^19.2.1",
"@ngrx/store": "^19.2.1",
"@ngrx/store-devtools": "^19.2.1",
"rxjs": "~7.8.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": {
"version": "3.0.0",
"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/router": "^20.0.0",
"@ngrx/signals": "^19.2.1",
"@ngrx/store": "^19.2.1",
"@ngrx/store-devtools": "^19.2.1",
"rxjs": "~7.8.0",
"tslib": "^2.3.0"
},

View file

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

View file

@ -1,5 +1,8 @@
<main>
@switch (currentView()) {
@case ('startingPoint') {
<app-starting-point></app-starting-point>
}
@default {
<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 = {
currentView: CurrentView;
name: string;
gender: Gender;
afterHighSchool: AfterHighSchool;
};
export enum CurrentView {
StartingPoint = 'startingPoint',
CollegeSelection = 'collegeSelection',
FirstJobHighSchoolDiploma = 'firstJobHighSchoolDiploma',
}
const initialState: AppState = {
currentView: CurrentView.StartingPoint,
name: '',
gender: Gender.Undefined,
afterHighSchool: AfterHighSchool.Undefined,
};
export const AppStateStore = signalStore(
@ -17,9 +26,31 @@ export const AppStateStore = signalStore(
withState(initialState),
withMethods(store => ({
updateCurrentView: (currentView: CurrentView) =>
patchState(store, () => ({
...store,
patchState(store, state => ({
...state,
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>
Hello {{ name() }}
</h1>
<form [formGroup]="form">
<label for="name">Your Name: </label>
<input id="name" type="text" formControlName="name">
<form [formGroup]="form" (submit)="onSubmit()">
<div class="name">
<label for="name">Your Name: </label>
<input id="name" type="text" formControlName="name">
</div>
<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>

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 = {
name: string;
@ -33,5 +34,12 @@ export const StartingPointStateStore = signalStore(
gender: gender ?? Gender.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 { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
import { StartingPointState, StartingPointStateStore } from './starting-point.state';
import { AppStateStore, CurrentView } from '../../app.state';
import { getState } from '@ngrx/signals';
export interface StartingPointForm {
name: string;
@ -30,6 +32,7 @@ export enum AfterHighSchool {
providers: [StartingPointStateStore],
})
export class StartingPoint implements OnInit {
private readonly globalState = inject(AppStateStore);
private readonly state = inject(StartingPointStateStore);
readonly name = this.state.name;
@ -40,6 +43,28 @@ export class StartingPoint implements OnInit {
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() {
this.form.valueChanges
.pipe(takeUntilDestroyed())
@ -49,4 +74,19 @@ export class StartingPoint implements OnInit {
}
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": [
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.spec.json"
}
]
}