diff --git a/angular.json b/angular.json index 561d26b..e011b83 100644 --- a/angular.json +++ b/angular.json @@ -34,7 +34,7 @@ } } }, - "angular-redux-demo": { + "angular-redux-simple-demo": { "projectType": "application", "schematics": { "@schematics/angular:component": { @@ -64,25 +64,25 @@ "skipTests": true } }, - "root": "projects/angular-redux-demo", - "sourceRoot": "projects/angular-redux-demo/src", + "root": "projects/angular-redux-simple-demo", + "sourceRoot": "projects/angular-redux-simple-demo/src", "prefix": "app", "architect": { "build": { "builder": "@angular-devkit/build-angular:application", "options": { - "outputPath": "dist/angular-redux-demo", - "index": "projects/angular-redux-demo/src/index.html", - "browser": "projects/angular-redux-demo/src/main.ts", + "outputPath": "dist/angular-redux-simple-demo", + "index": "projects/angular-redux-simple-demo/src/index.html", + "browser": "projects/angular-redux-simple-demo/src/main.ts", "polyfills": ["zone.js"], - "tsConfig": "projects/angular-redux-demo/tsconfig.app.json", + "tsConfig": "projects/angular-redux-simple-demo/tsconfig.app.json", "assets": [ { "glob": "**/*", - "input": "projects/angular-redux-demo/public" + "input": "projects/angular-redux-simple-demo/public" } ], - "styles": ["projects/angular-redux-demo/src/styles.css"], + "styles": ["projects/angular-redux-simple-demo/src/styles.css"], "scripts": [] }, "configurations": { @@ -113,10 +113,79 @@ "builder": "@angular-devkit/build-angular:dev-server", "configurations": { "production": { - "buildTarget": "angular-redux-demo:build:production" + "buildTarget": "angular-redux-simple-demo:build:production" }, "development": { - "buildTarget": "angular-redux-demo:build:development" + "buildTarget": "angular-redux-simple-demo:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n" + } + } + }, + "angular-redux-injector-demo": { + "projectType": "application", + "schematics": {}, + "root": "projects/angular-redux-injector-demo", + "sourceRoot": "projects/angular-redux-injector-demo/src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:application", + "options": { + "outputPath": "dist/angular-redux-injector-demo", + "index": "projects/angular-redux-injector-demo/src/index.html", + "browser": "projects/angular-redux-injector-demo/src/main.ts", + "polyfills": [ + "zone.js" + ], + "tsConfig": "projects/angular-redux-injector-demo/tsconfig.app.json", + "assets": [ + { + "glob": "**/*", + "input": "projects/angular-redux-injector-demo/public" + } + ], + "styles": [ + "projects/angular-redux-injector-demo/src/styles.css" + ], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kB", + "maximumError": "1MB" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kB", + "maximumError": "4kB" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-redux-injector-demo:build:production" + }, + "development": { + "buildTarget": "angular-redux-injector-demo:build:development" } }, "defaultConfiguration": "development" diff --git a/projects/angular-redux-demo/public/favicon.ico b/projects/angular-redux-injector-demo/public/favicon.ico similarity index 100% rename from projects/angular-redux-demo/public/favicon.ico rename to projects/angular-redux-injector-demo/public/favicon.ico diff --git a/projects/angular-redux-injector-demo/src/app/app.component.ts b/projects/angular-redux-injector-demo/src/app/app.component.ts new file mode 100644 index 0000000..bd9a80f --- /dev/null +++ b/projects/angular-redux-injector-demo/src/app/app.component.ts @@ -0,0 +1,27 @@ +import { Component, EnvironmentInjector, inject } from '@angular/core'; +import { injectSelector, injectDispatch } from '@reduxjs/angular-redux'; +import { incrementByRandomNumber } from './store/counter-slice'; +import { AppDispatch, RootState } from './store'; + +@Component({ + selector: 'app-root', + standalone: true, + template: ` + +

{{ count() }}

+ @if (isLoading()) { +

Loading...

+ } + `, +}) +export class AppComponent { + injector = inject(EnvironmentInjector); + count = injectSelector((state: RootState) => state.counter.value); + isLoading = injectSelector((state: RootState) => state.counter.isLoading); + dispatch = injectDispatch(); + incrementByRandomNumber = () => { + this.dispatch(incrementByRandomNumber({ injector: this.injector })); + }; +} diff --git a/projects/angular-redux-demo/src/app/app.config.ts b/projects/angular-redux-injector-demo/src/app/app.config.ts similarity index 100% rename from projects/angular-redux-demo/src/app/app.config.ts rename to projects/angular-redux-injector-demo/src/app/app.config.ts diff --git a/projects/angular-redux-injector-demo/src/app/services/random-number.service.ts b/projects/angular-redux-injector-demo/src/app/services/random-number.service.ts new file mode 100644 index 0000000..1fc4506 --- /dev/null +++ b/projects/angular-redux-injector-demo/src/app/services/random-number.service.ts @@ -0,0 +1,12 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ providedIn: 'root' }) +export class RandomNumberService { + getRandomNumber() { + return new Promise((resolve) => { + setTimeout(() => { + resolve(Math.floor(Math.random() * 100)); + }, 1000); + }); + } +} diff --git a/projects/angular-redux-injector-demo/src/app/store/counter-slice.ts b/projects/angular-redux-injector-demo/src/app/store/counter-slice.ts new file mode 100644 index 0000000..ba67c31 --- /dev/null +++ b/projects/angular-redux-injector-demo/src/app/store/counter-slice.ts @@ -0,0 +1,38 @@ +import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; +import { inject } from '@angular/core'; +import { RandomNumberService } from '../services/random-number.service'; +import { + asyncRunInInjectionContext, + RunInInjectionContextProps, +} from '../utils/async-run-in-injection-context'; + +export const incrementByRandomNumber = createAsyncThunk( + 'counter/incrementByAmountFromService', + (arg: RunInInjectionContextProps<{}>, _thunkAPI) => { + return asyncRunInInjectionContext(arg.injector, async () => { + const service = inject(RandomNumberService); + const newCount = await service.getRandomNumber(); + return newCount; + }); + }, +); + +export const counterSlice = createSlice({ + name: 'counter', + initialState: { + value: 0, + isLoading: false, + }, + reducers: {}, + extraReducers: (builder) => { + builder.addCase(incrementByRandomNumber.fulfilled, (state, action) => { + state.isLoading = false; + state.value += action.payload; + }); + builder.addCase(incrementByRandomNumber.pending, (state) => { + state.isLoading = true; + }); + }, +}); + +export default counterSlice.reducer; diff --git a/projects/angular-redux-demo/src/app/store/index.ts b/projects/angular-redux-injector-demo/src/app/store/index.ts similarity index 100% rename from projects/angular-redux-demo/src/app/store/index.ts rename to projects/angular-redux-injector-demo/src/app/store/index.ts diff --git a/projects/angular-redux-injector-demo/src/app/utils/async-run-in-injection-context.ts b/projects/angular-redux-injector-demo/src/app/utils/async-run-in-injection-context.ts new file mode 100644 index 0000000..e188b4e --- /dev/null +++ b/projects/angular-redux-injector-demo/src/app/utils/async-run-in-injection-context.ts @@ -0,0 +1,24 @@ +import { EnvironmentInjector, runInInjectionContext } from '@angular/core'; + +export const asyncRunInInjectionContext = ( + injector: EnvironmentInjector, + fn: () => Promise, +) => { + return new Promise((resolve, reject) => { + runInInjectionContext(injector, () => { + fn() + .then((value) => { + resolve(value); + }) + .catch((error) => { + reject(error); + }); + }); + }); +}; + +export type RunInInjectionContextProps< + T extends object, +> = T & { + injector: EnvironmentInjector; +}; diff --git a/projects/angular-redux-injector-demo/src/index.html b/projects/angular-redux-injector-demo/src/index.html new file mode 100644 index 0000000..b17f9d3 --- /dev/null +++ b/projects/angular-redux-injector-demo/src/index.html @@ -0,0 +1,13 @@ + + + + + AngularReduxInjectorDemo + + + + + + + + diff --git a/projects/angular-redux-injector-demo/src/main.ts b/projects/angular-redux-injector-demo/src/main.ts new file mode 100644 index 0000000..35b00f3 --- /dev/null +++ b/projects/angular-redux-injector-demo/src/main.ts @@ -0,0 +1,6 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { appConfig } from './app/app.config'; +import { AppComponent } from './app/app.component'; + +bootstrapApplication(AppComponent, appConfig) + .catch((err) => console.error(err)); diff --git a/projects/angular-redux-demo/src/styles.css b/projects/angular-redux-injector-demo/src/styles.css similarity index 100% rename from projects/angular-redux-demo/src/styles.css rename to projects/angular-redux-injector-demo/src/styles.css diff --git a/projects/angular-redux-injector-demo/tsconfig.app.json b/projects/angular-redux-injector-demo/tsconfig.app.json new file mode 100644 index 0000000..e40712b --- /dev/null +++ b/projects/angular-redux-injector-demo/tsconfig.app.json @@ -0,0 +1,15 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/app", + "types": [] + }, + "files": [ + "src/main.ts" + ], + "include": [ + "src/**/*.d.ts" + ] +} diff --git a/projects/angular-redux-simple-demo/public/favicon.ico b/projects/angular-redux-simple-demo/public/favicon.ico new file mode 100644 index 0000000..57614f9 Binary files /dev/null and b/projects/angular-redux-simple-demo/public/favicon.ico differ diff --git a/projects/angular-redux-demo/src/app/app.component.ts b/projects/angular-redux-simple-demo/src/app/app.component.ts similarity index 100% rename from projects/angular-redux-demo/src/app/app.component.ts rename to projects/angular-redux-simple-demo/src/app/app.component.ts diff --git a/projects/angular-redux-simple-demo/src/app/app.config.ts b/projects/angular-redux-simple-demo/src/app/app.config.ts new file mode 100644 index 0000000..8f1092f --- /dev/null +++ b/projects/angular-redux-simple-demo/src/app/app.config.ts @@ -0,0 +1,10 @@ +import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; +import { provideRedux } from '@reduxjs/angular-redux'; +import { store } from './store'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideZoneChangeDetection({ eventCoalescing: true }), + provideRedux({ store }), + ], +}; diff --git a/projects/angular-redux-demo/src/app/store/counter-slice.ts b/projects/angular-redux-simple-demo/src/app/store/counter-slice.ts similarity index 100% rename from projects/angular-redux-demo/src/app/store/counter-slice.ts rename to projects/angular-redux-simple-demo/src/app/store/counter-slice.ts diff --git a/projects/angular-redux-simple-demo/src/app/store/index.ts b/projects/angular-redux-simple-demo/src/app/store/index.ts new file mode 100644 index 0000000..0e410ac --- /dev/null +++ b/projects/angular-redux-simple-demo/src/app/store/index.ts @@ -0,0 +1,13 @@ +import { configureStore } from '@reduxjs/toolkit'; +import counterReducer from './counter-slice'; + +export const store = configureStore({ + reducer: { + counter: counterReducer, + }, +}); + +// Infer the `RootState` and `AppDispatch` types from the store itself +export type RootState = ReturnType; +// Inferred type: {counter: CounterState} +export type AppDispatch = typeof store.dispatch; diff --git a/projects/angular-redux-demo/src/index.html b/projects/angular-redux-simple-demo/src/index.html similarity index 100% rename from projects/angular-redux-demo/src/index.html rename to projects/angular-redux-simple-demo/src/index.html diff --git a/projects/angular-redux-demo/src/main.ts b/projects/angular-redux-simple-demo/src/main.ts similarity index 100% rename from projects/angular-redux-demo/src/main.ts rename to projects/angular-redux-simple-demo/src/main.ts diff --git a/projects/angular-redux-simple-demo/src/styles.css b/projects/angular-redux-simple-demo/src/styles.css new file mode 100644 index 0000000..90d4ee0 --- /dev/null +++ b/projects/angular-redux-simple-demo/src/styles.css @@ -0,0 +1 @@ +/* You can add global styles to this file, and also import other style files */ diff --git a/projects/angular-redux-demo/tsconfig.app.json b/projects/angular-redux-simple-demo/tsconfig.app.json similarity index 100% rename from projects/angular-redux-demo/tsconfig.app.json rename to projects/angular-redux-simple-demo/tsconfig.app.json