Skip to content

Commit d383822

Browse files
committed
chore: add runInInjectionContextDemo
1 parent 9f4176e commit d383822

File tree

11 files changed

+240
-0
lines changed

11 files changed

+240
-0
lines changed

angular.json

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,95 @@
125125
"builder": "@angular-devkit/build-angular:extract-i18n"
126126
}
127127
}
128+
},
129+
"angular-redux-injector-demo": {
130+
"projectType": "application",
131+
"schematics": {},
132+
"root": "projects/angular-redux-injector-demo",
133+
"sourceRoot": "projects/angular-redux-injector-demo/src",
134+
"prefix": "app",
135+
"architect": {
136+
"build": {
137+
"builder": "@angular-devkit/build-angular:application",
138+
"options": {
139+
"outputPath": "dist/angular-redux-injector-demo",
140+
"index": "projects/angular-redux-injector-demo/src/index.html",
141+
"browser": "projects/angular-redux-injector-demo/src/main.ts",
142+
"polyfills": [
143+
"zone.js"
144+
],
145+
"tsConfig": "projects/angular-redux-injector-demo/tsconfig.app.json",
146+
"assets": [
147+
{
148+
"glob": "**/*",
149+
"input": "projects/angular-redux-injector-demo/public"
150+
}
151+
],
152+
"styles": [
153+
"projects/angular-redux-injector-demo/src/styles.css"
154+
],
155+
"scripts": []
156+
},
157+
"configurations": {
158+
"production": {
159+
"budgets": [
160+
{
161+
"type": "initial",
162+
"maximumWarning": "500kB",
163+
"maximumError": "1MB"
164+
},
165+
{
166+
"type": "anyComponentStyle",
167+
"maximumWarning": "2kB",
168+
"maximumError": "4kB"
169+
}
170+
],
171+
"outputHashing": "all"
172+
},
173+
"development": {
174+
"optimization": false,
175+
"extractLicenses": false,
176+
"sourceMap": true
177+
}
178+
},
179+
"defaultConfiguration": "production"
180+
},
181+
"serve": {
182+
"builder": "@angular-devkit/build-angular:dev-server",
183+
"configurations": {
184+
"production": {
185+
"buildTarget": "angular-redux-injector-demo:build:production"
186+
},
187+
"development": {
188+
"buildTarget": "angular-redux-injector-demo:build:development"
189+
}
190+
},
191+
"defaultConfiguration": "development"
192+
},
193+
"extract-i18n": {
194+
"builder": "@angular-devkit/build-angular:extract-i18n"
195+
},
196+
"test": {
197+
"builder": "@angular-devkit/build-angular:karma",
198+
"options": {
199+
"polyfills": [
200+
"zone.js",
201+
"zone.js/testing"
202+
],
203+
"tsConfig": "projects/angular-redux-injector-demo/tsconfig.spec.json",
204+
"assets": [
205+
{
206+
"glob": "**/*",
207+
"input": "projects/angular-redux-injector-demo/public"
208+
}
209+
],
210+
"styles": [
211+
"projects/angular-redux-injector-demo/src/styles.css"
212+
],
213+
"scripts": []
214+
}
215+
}
216+
}
128217
}
129218
}
130219
}
Binary file not shown.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Component, EnvironmentInjector, inject } from '@angular/core';
2+
import { injectSelector, injectDispatch } from '@reduxjs/angular-redux';
3+
import { incrementByRandomNumber } from './store/counter-slice';
4+
import { AppDispatch, RootState } from './store';
5+
6+
@Component({
7+
selector: 'app-root',
8+
standalone: true,
9+
template: `
10+
<button (click)="incrementByRandomNumber()">
11+
Increment by a random number
12+
</button>
13+
<p>{{ count() }}</p>
14+
@if (isLoading()) {
15+
<p>Loading...</p>
16+
}
17+
`,
18+
})
19+
export class AppComponent {
20+
injector = inject(EnvironmentInjector);
21+
count = injectSelector((state: RootState) => state.counter.value);
22+
isLoading = injectSelector((state: RootState) => state.counter.isLoading);
23+
dispatch = injectDispatch<AppDispatch>();
24+
incrementByRandomNumber = () => {
25+
this.dispatch(incrementByRandomNumber({ injector: this.injector }));
26+
};
27+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
2+
import { provideRedux } from '@reduxjs/angular-redux';
3+
import { store } from './store';
4+
5+
export const appConfig: ApplicationConfig = {
6+
providers: [
7+
provideZoneChangeDetection({ eventCoalescing: true }),
8+
provideRedux({ store }),
9+
],
10+
};
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Injectable } from '@angular/core';
2+
3+
@Injectable({ providedIn: 'root' })
4+
export class RandomNumberService {
5+
getRandomNumber() {
6+
return new Promise<number>((resolve) => {
7+
setTimeout(() => {
8+
resolve(Math.floor(Math.random() * 100));
9+
}, 1000);
10+
});
11+
}
12+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
2+
import {
3+
EnvironmentInjector,
4+
inject,
5+
runInInjectionContext,
6+
} from '@angular/core';
7+
import { RandomNumberService } from '../services/random-number.service';
8+
9+
interface IncrementByAmountFromServiceProps {
10+
injector: EnvironmentInjector;
11+
}
12+
13+
export const incrementByRandomNumber = createAsyncThunk(
14+
'counter/incrementByAmountFromService',
15+
(arg: IncrementByAmountFromServiceProps, _thunkAPI) => {
16+
return new Promise<number>((resolve, reject) => {
17+
runInInjectionContext(arg.injector, () => {
18+
const runValue = async () => {
19+
const service = inject(RandomNumberService);
20+
const newCount = await service.getRandomNumber();
21+
return newCount;
22+
};
23+
24+
runValue()
25+
.then((value) => {
26+
resolve(value);
27+
})
28+
.catch((error) => {
29+
reject(error);
30+
});
31+
});
32+
});
33+
},
34+
);
35+
36+
export const counterSlice = createSlice({
37+
name: 'counter',
38+
initialState: {
39+
value: 0,
40+
isLoading: false,
41+
},
42+
reducers: {},
43+
extraReducers: (builder) => {
44+
builder.addCase(incrementByRandomNumber.fulfilled, (state, action) => {
45+
state.isLoading = false;
46+
state.value += action.payload;
47+
});
48+
builder.addCase(incrementByRandomNumber.pending, (state) => {
49+
state.isLoading = true;
50+
});
51+
},
52+
});
53+
54+
export default counterSlice.reducer;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { configureStore } from '@reduxjs/toolkit';
2+
import counterReducer from './counter-slice';
3+
4+
export const store = configureStore({
5+
reducer: {
6+
counter: counterReducer,
7+
},
8+
});
9+
10+
// Infer the `RootState` and `AppDispatch` types from the store itself
11+
export type RootState = ReturnType<typeof store.getState>;
12+
// Inferred type: {counter: CounterState}
13+
export type AppDispatch = typeof store.dispatch;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<title>AngularReduxInjectorDemo</title>
6+
<base href="/">
7+
<meta name="viewport" content="width=device-width, initial-scale=1">
8+
<link rel="icon" type="image/x-icon" href="favicon.ico">
9+
</head>
10+
<body>
11+
<app-root></app-root>
12+
</body>
13+
</html>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { bootstrapApplication } from '@angular/platform-browser';
2+
import { appConfig } from './app/app.config';
3+
import { AppComponent } from './app/app.component';
4+
5+
bootstrapApplication(AppComponent, appConfig)
6+
.catch((err) => console.error(err));
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/* You can add global styles to this file, and also import other style files */

0 commit comments

Comments
 (0)