├── README.md ├── .github ├── FUNDING.yml └── workflows │ └── test.yml ├── projects ├── angular-redux │ ├── schematics-core │ │ ├── utility │ │ │ ├── update.ts │ │ │ ├── json-utilts.ts │ │ │ ├── parse-name.ts │ │ │ ├── package.ts │ │ │ ├── project.ts │ │ │ ├── config.ts │ │ │ ├── find-module.ts │ │ │ ├── find-component.ts │ │ │ ├── strings.ts │ │ │ ├── change.ts │ │ │ └── visitors.ts │ │ ├── testing │ │ │ ├── update.ts │ │ │ ├── index.ts │ │ │ ├── create-package.ts │ │ │ ├── create-reducers.ts │ │ │ ├── create-app-module.ts │ │ │ └── create-workspace.ts │ │ ├── README.md │ │ └── index.ts │ ├── src │ │ ├── lib │ │ │ ├── types.ts │ │ │ ├── provide-redux.ts │ │ │ ├── utils │ │ │ │ ├── shallowEqual.ts │ │ │ │ └── Subscription.ts │ │ │ ├── provider.ts │ │ │ ├── inject-dispatch.ts │ │ │ ├── inject-store.ts │ │ │ └── inject-selector.ts │ │ ├── public-api.ts │ │ └── tests │ │ │ ├── inject-dispatch.spec.ts │ │ │ ├── utils │ │ │ ├── Subscription.spec.ts │ │ │ └── shallowEqual.spec.ts │ │ │ ├── injections.withTypes.spec.ts │ │ │ ├── inject-selector-and-dispatch.spec.ts │ │ │ └── inject-selector.spec.ts │ ├── schematics │ │ ├── ng-add │ │ │ ├── schema.ts │ │ │ ├── files │ │ │ │ └── __storePath__ │ │ │ │ │ ├── index.ts.template │ │ │ │ │ └── counter-slice.ts.template │ │ │ ├── schema.json │ │ │ ├── index.spec.ts │ │ │ └── index.ts │ │ ├── collection.json │ │ ├── tsconfig.spec.json │ │ └── jest.config.mjs │ ├── ng-package.json │ ├── tsconfig.lib.prod.json │ ├── tsconfig.lib.json │ ├── tsconfig.spec.json │ ├── tsconfig.schematics.json │ ├── package.json │ └── README.md ├── angular-redux-simple-demo │ ├── src │ │ ├── styles.css │ │ ├── main.ts │ │ ├── app │ │ │ ├── app.config.ts │ │ │ ├── store │ │ │ │ ├── index.ts │ │ │ │ └── counter-slice.ts │ │ │ └── app.component.ts │ │ └── index.html │ ├── public │ │ └── favicon.ico │ └── tsconfig.app.json ├── angular-redux-injector-demo │ ├── src │ │ ├── styles.css │ │ ├── main.ts │ │ ├── app │ │ │ ├── services │ │ │ │ └── random-number.service.ts │ │ │ ├── app.config.ts │ │ │ ├── store │ │ │ │ ├── index.ts │ │ │ │ └── counter-slice.ts │ │ │ ├── utils │ │ │ │ └── async-run-in-injection-context.ts │ │ │ └── app.component.ts │ │ └── index.html │ ├── public │ │ └── favicon.ico │ └── tsconfig.app.json └── angular-redux-auto-dispatch-demo │ ├── src │ ├── styles.css │ ├── main.ts │ ├── app │ │ ├── app.config.ts │ │ ├── store │ │ │ ├── index.ts │ │ │ └── counter-slice.ts │ │ └── app.component.ts │ └── index.html │ ├── public │ └── favicon.ico │ └── tsconfig.app.json ├── CHANGELOG.md ├── website ├── static │ ├── img │ │ ├── favicon │ │ │ └── favicon.ico │ │ ├── redux-logo-landscape.png │ │ ├── redux-logo-twitter.png │ │ ├── noun_Check_1870817.svg │ │ ├── external-link-square-alt-solid.svg │ │ ├── redux.svg │ │ ├── redux_white.svg │ │ ├── github-brands.svg │ │ ├── noun_Box_1664404.svg │ │ ├── noun_Rocket_1245262.svg │ │ └── noun_Certificate_1945625.svg │ ├── scripts │ │ ├── sidebarScroll.js │ │ ├── monokaiTheme.js │ │ └── codeblock.js │ └── css │ │ ├── codeblock.css │ │ ├── 404.css │ │ └── custom.css ├── .gitignore ├── sidebars.js ├── package.json ├── src │ ├── pages │ │ ├── styles.module.css │ │ └── index.js │ └── theme │ │ └── NotFound.js ├── README.md └── docusaurus.config.js ├── .yarnrc.yml ├── .vscode ├── extensions.json ├── launch.json └── tasks.json ├── .editorconfig ├── netlify.toml ├── .gitignore ├── LICENSE.md ├── tsconfig.json ├── CODE_OF_CONDUCT.md ├── package.json ├── CONTRIBUTING.md ├── docs ├── introduction │ └── getting-started.md └── tutorials │ └── typescript.md └── angular.json /README.md: -------------------------------------------------------------------------------- 1 | ./projects/angular-redux/README.md -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [crutchcorn] 2 | -------------------------------------------------------------------------------- /projects/angular-redux/schematics-core/utility/update.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /projects/angular-redux/src/lib/types.ts: -------------------------------------------------------------------------------- 1 | export type EqualityFn = (a: T, b: T) => boolean; 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | All notable changes are described on the [Releases](https://github.com/reduxjs/react-redux/releases) page. 2 | -------------------------------------------------------------------------------- /projects/angular-redux-simple-demo/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /website/static/img/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reduxjs/angular-redux/HEAD/website/static/img/favicon/favicon.ico -------------------------------------------------------------------------------- /projects/angular-redux-injector-demo/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /projects/angular-redux-auto-dispatch-demo/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /website/static/img/redux-logo-landscape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reduxjs/angular-redux/HEAD/website/static/img/redux-logo-landscape.png -------------------------------------------------------------------------------- /website/static/img/redux-logo-twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reduxjs/angular-redux/HEAD/website/static/img/redux-logo-twitter.png -------------------------------------------------------------------------------- /projects/angular-redux/schematics-core/testing/update.ts: -------------------------------------------------------------------------------- 1 | export const upgradeVersion = '6.0.0'; 2 | export const versionPrefixes = ['~', '^', '']; 3 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | compressionLevel: mixed 2 | 3 | enableGlobalCache: false 4 | 5 | nodeLinker: node-modules 6 | 7 | yarnPath: .yarn/releases/yarn-4.1.0.cjs 8 | -------------------------------------------------------------------------------- /projects/angular-redux-simple-demo/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reduxjs/angular-redux/HEAD/projects/angular-redux-simple-demo/public/favicon.ico -------------------------------------------------------------------------------- /projects/angular-redux-injector-demo/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reduxjs/angular-redux/HEAD/projects/angular-redux-injector-demo/public/favicon.ico -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 3 | "recommendations": ["angular.ng-template"] 4 | } 5 | -------------------------------------------------------------------------------- /projects/angular-redux-auto-dispatch-demo/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reduxjs/angular-redux/HEAD/projects/angular-redux-auto-dispatch-demo/public/favicon.ico -------------------------------------------------------------------------------- /projects/angular-redux/schematics-core/testing/index.ts: -------------------------------------------------------------------------------- 1 | export * from './create-app-module'; 2 | export * from './create-reducers'; 3 | export * from './create-workspace'; 4 | -------------------------------------------------------------------------------- /projects/angular-redux/schematics/ng-add/schema.ts: -------------------------------------------------------------------------------- 1 | export interface Schema { 2 | skipPackageJson?: boolean; 3 | path?: string; 4 | project?: string; 5 | module?: string; 6 | storePath?: string; 7 | } 8 | -------------------------------------------------------------------------------- /projects/angular-redux/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/angular-redux", 4 | "lib": { 5 | "entryFile": "src/public-api.ts" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /projects/angular-redux/schematics-core/README.md: -------------------------------------------------------------------------------- 1 | This code is originally from NgRx: 2 | 3 | https://github.com/ngrx/platform/tree/main/modules/schematics-core 4 | https://github.com/ngrx/platform/tree/main/modules/store/schematics-core 5 | -------------------------------------------------------------------------------- /projects/angular-redux/schematics/collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "schematics": { 3 | "ng-add": { 4 | "aliases": ["init"], 5 | "factory": "./ng-add", 6 | "schema": "./ng-add/schema.json", 7 | "description": "Adds initial setup for state managment" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /projects/angular-redux/schematics/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "emitDecoratorMetadata": true, 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /projects/angular-redux-injector-demo/src/main.ts: -------------------------------------------------------------------------------- 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).catch((err) => 6 | console.error(err), 7 | ); 8 | -------------------------------------------------------------------------------- /projects/angular-redux-simple-demo/src/main.ts: -------------------------------------------------------------------------------- 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).catch((err) => 6 | console.error(err), 7 | ); 8 | -------------------------------------------------------------------------------- /projects/angular-redux-auto-dispatch-demo/src/main.ts: -------------------------------------------------------------------------------- 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).catch((err) => 6 | console.error(err), 7 | ); 8 | -------------------------------------------------------------------------------- /website/static/img/noun_Check_1870817.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /website/.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | /package.lock.json 4 | 5 | # production 6 | /build 7 | 8 | # generated files 9 | .docusaurus/ 10 | website/.docusaurus/ 11 | .cache-loader 12 | 13 | # misc 14 | .DS_Store 15 | .env.local 16 | .env.development.local 17 | .env.test.local 18 | .env.production.local 19 | 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | -------------------------------------------------------------------------------- /projects/angular-redux/schematics/jest.config.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | displayName: "Schematics", 3 | coverageDirectory: "../../coverage/modules/schematics", 4 | transform: { 5 | "^.+\\.(ts|mjs|js)$": [ 6 | "ts-jest", 7 | { 8 | tsconfig: "/tsconfig.spec.json", 9 | }, 10 | ], 11 | }, 12 | transformIgnorePatterns: ["node_modules/(?!.*\\.mjs$)"], 13 | }; 14 | -------------------------------------------------------------------------------- /projects/angular-redux-injector-demo/src/app/services/random-number.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable({ providedIn: 'root' }) 4 | export class RandomNumberService { 5 | getRandomNumber() { 6 | return new Promise((resolve) => { 7 | setTimeout(() => { 8 | resolve(Math.floor(Math.random() * 100)); 9 | }, 1000); 10 | }); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /projects/angular-redux/schematics/ng-add/files/__storePath__/index.ts.template: -------------------------------------------------------------------------------- 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 | export type RootState = ReturnType 11 | export type AppDispatch = typeof store.dispatch 12 | -------------------------------------------------------------------------------- /projects/angular-redux-simple-demo/src/app/app.config.ts: -------------------------------------------------------------------------------- 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 | }; 11 | -------------------------------------------------------------------------------- /projects/angular-redux-injector-demo/src/app/app.config.ts: -------------------------------------------------------------------------------- 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 | }; 11 | -------------------------------------------------------------------------------- /website/sidebars.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | docs: [ 3 | { 4 | type: "category", 5 | label: "Introduction", 6 | collapsed: false, 7 | items: ["introduction/getting-started"], 8 | }, 9 | { 10 | type: "category", 11 | label: "Tutorials", 12 | collapsed: false, 13 | items: ["tutorials/quick-start", "tutorials/typescript-quick-start"], 14 | }, 15 | ], 16 | }; 17 | -------------------------------------------------------------------------------- /projects/angular-redux-auto-dispatch-demo/src/app/app.config.ts: -------------------------------------------------------------------------------- 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 | }; 11 | -------------------------------------------------------------------------------- /projects/angular-redux/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of @reduxjs/angular-redux 3 | */ 4 | 5 | export * from './lib/inject-dispatch'; 6 | export * from './lib/inject-selector'; 7 | export * from './lib/inject-store'; 8 | export * from './lib/provide-redux'; 9 | export * from './lib/provider'; 10 | export * from './lib/utils/shallowEqual'; 11 | export type { Subscription } from './lib/utils/Subscription'; 12 | -------------------------------------------------------------------------------- /projects/angular-redux-simple-demo/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AngularReduxDemo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /projects/angular-redux-auto-dispatch-demo/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AngularReduxDemo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /projects/angular-redux-injector-demo/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AngularReduxInjectorDemo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [push, pull_request, workflow_dispatch] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v4 11 | - uses: actions/setup-node@v4 12 | with: 13 | node-version: "20.x" 14 | cache: "yarn" 15 | 16 | - name: Install packages 17 | run: yarn install 18 | 19 | - name: Run tests 20 | run: yarn test 21 | -------------------------------------------------------------------------------- /projects/angular-redux/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ 2 | /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ 3 | { 4 | "extends": "./tsconfig.lib.json", 5 | "compilerOptions": { 6 | "declarationMap": false 7 | }, 8 | "angularCompilerOptions": { 9 | "compilationMode": "partial" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /projects/angular-redux/schematics-core/utility/json-utilts.ts: -------------------------------------------------------------------------------- 1 | // https://github.com/angular/angular-cli/blob/master/packages/schematics/angular/utility/json-utils.ts 2 | export function findPropertyInAstObject( 3 | node: any, 4 | propertyName: string, 5 | ): any | null { 6 | let maybeNode: any | null = null; 7 | for (const property of node.properties) { 8 | if (property.key.value == propertyName) { 9 | maybeNode = property.value; 10 | } 11 | } 12 | 13 | return maybeNode; 14 | } 15 | -------------------------------------------------------------------------------- /projects/angular-redux-injector-demo/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ 2 | /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ 3 | { 4 | "extends": "../../tsconfig.json", 5 | "compilerOptions": { 6 | "outDir": "../../out-tsc/app", 7 | "types": [] 8 | }, 9 | "files": ["src/main.ts"], 10 | "include": ["src/**/*.d.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /projects/angular-redux-simple-demo/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ 2 | /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ 3 | { 4 | "extends": "../../tsconfig.json", 5 | "compilerOptions": { 6 | "outDir": "../../out-tsc/app", 7 | "types": [] 8 | }, 9 | "files": ["src/main.ts"], 10 | "include": ["src/**/*.d.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /projects/angular-redux-simple-demo/src/app/store/index.ts: -------------------------------------------------------------------------------- 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; 12 | // Inferred type: {counter: CounterState} 13 | export type AppDispatch = typeof store.dispatch; 14 | -------------------------------------------------------------------------------- /projects/angular-redux-injector-demo/src/app/store/index.ts: -------------------------------------------------------------------------------- 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; 12 | // Inferred type: {counter: CounterState} 13 | export type AppDispatch = typeof store.dispatch; 14 | -------------------------------------------------------------------------------- /projects/angular-redux/schematics-core/utility/parse-name.ts: -------------------------------------------------------------------------------- 1 | import { Path, basename, dirname, normalize } from '@angular-devkit/core'; 2 | 3 | export interface Location { 4 | name: string; 5 | path: Path; 6 | } 7 | 8 | export function parseName(path: string, name: string): Location { 9 | const nameWithoutPath = basename(name as Path); 10 | const namePath = dirname((path + '/' + name) as Path); 11 | 12 | return { 13 | name: nameWithoutPath, 14 | path: normalize('/' + namePath), 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /projects/angular-redux-auto-dispatch-demo/src/app/store/index.ts: -------------------------------------------------------------------------------- 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; 12 | // Inferred type: {counter: CounterState} 13 | export type AppDispatch = typeof store.dispatch; 14 | -------------------------------------------------------------------------------- /projects/angular-redux-auto-dispatch-demo/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ 2 | /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ 3 | { 4 | "extends": "../../tsconfig.json", 5 | "compilerOptions": { 6 | "outDir": "../../out-tsc/app", 7 | "types": [] 8 | }, 9 | "files": [ 10 | "src/main.ts" 11 | ], 12 | "include": [ 13 | "src/**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /projects/angular-redux/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ 2 | /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ 3 | { 4 | "extends": "../../tsconfig.json", 5 | "compilerOptions": { 6 | "outDir": "../../out-tsc/lib", 7 | "declaration": true, 8 | "declarationMap": true, 9 | "inlineSources": true, 10 | "types": [] 11 | }, 12 | "exclude": ["**/*.spec.ts"] 13 | } 14 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "name": "ng serve", 7 | "type": "chrome", 8 | "request": "launch", 9 | "preLaunchTask": "npm: start", 10 | "url": "http://localhost:4200/" 11 | }, 12 | { 13 | "name": "ng test", 14 | "type": "chrome", 15 | "request": "launch", 16 | "preLaunchTask": "npm: test", 17 | "url": "http://localhost:9876/debug.html" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | base = "website" 3 | publish = "build" 4 | command = "yarn && yarn build" 5 | ignore = "git diff --quiet HEAD^ HEAD -- ../docs/ . ../netlify.toml" 6 | 7 | [build.environment] 8 | NODE_VERSION = "20" 9 | NODE_OPTIONS = "--max_old_space_size=4096" 10 | NETLIFY_USE_YARN = "true" 11 | YARN_VERSION = "1.22.10" 12 | 13 | 14 | [[plugins]] 15 | package = "netlify-plugin-cache" 16 | [plugins.inputs] 17 | paths = [ 18 | "node_modules/.cache", 19 | "website/node_modules/.cache", 20 | ".yarn/.cache" 21 | ] 22 | 23 | -------------------------------------------------------------------------------- /projects/angular-redux/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ 2 | /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ 3 | { 4 | "extends": "../../tsconfig.json", 5 | "compilerOptions": { 6 | "outDir": "../../out-tsc/spec", 7 | "emitDecoratorMetadata": true, 8 | "types": ["jest"] 9 | }, 10 | "include": ["**/*.spec.ts", "**/*.d.ts"], 11 | "exclude": ["./schematics/**/*.ts", "./schematics-core/**/*.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /projects/angular-redux/src/lib/provide-redux.ts: -------------------------------------------------------------------------------- 1 | import type { Action, Store, UnknownAction } from 'redux'; 2 | import { createReduxProvider, ReduxProvider } from './provider'; 3 | 4 | export interface ProviderProps< 5 | A extends Action = UnknownAction, 6 | S = unknown, 7 | > { 8 | /** 9 | * The single Redux store in your application. 10 | */ 11 | store: Store; 12 | } 13 | 14 | export function provideRedux< 15 | A extends Action = UnknownAction, 16 | S = unknown, 17 | >({ store }: ProviderProps) { 18 | return { 19 | provide: ReduxProvider, 20 | useValue: createReduxProvider(store), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /website/static/img/external-link-square-alt-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /projects/angular-redux/schematics-core/testing/create-package.ts: -------------------------------------------------------------------------------- 1 | import { Tree } from '@angular-devkit/schematics'; 2 | import { 3 | UnitTestTree, 4 | SchematicTestRunner, 5 | } from '@angular-devkit/schematics/testing'; 6 | 7 | export const packagePath = '/package.json'; 8 | 9 | export function createPackageJson( 10 | prefix: string, 11 | pkg: string, 12 | tree: UnitTestTree, 13 | version = '5.2.0', 14 | packagePath = '/package.json', 15 | ) { 16 | tree.create( 17 | packagePath, 18 | `{ 19 | "dependencies": { 20 | "@ngrx/${pkg}": "${prefix}${version}" 21 | } 22 | }`, 23 | ); 24 | 25 | return tree; 26 | } 27 | -------------------------------------------------------------------------------- /website/static/scripts/sidebarScroll.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded", () => { 2 | // Find the active nav item in the sidebar 3 | const item = document.getElementsByClassName("navListItemActive")[0]; 4 | if (!item) { 5 | return; 6 | } 7 | const bounding = item.getBoundingClientRect(); 8 | if ( 9 | bounding.top >= 0 && 10 | bounding.bottom <= 11 | (window.innerHeight || document.documentElement.clientHeight) 12 | ) { 13 | // Already visible. Do nothing. 14 | } else { 15 | // Not visible. Scroll sidebar. 16 | item.scrollIntoView({ block: "center", inline: "nearest" }); 17 | document.body.scrollTop = document.documentElement.scrollTop = 0; 18 | } 19 | }); 20 | -------------------------------------------------------------------------------- /projects/angular-redux-injector-demo/src/app/utils/async-run-in-injection-context.ts: -------------------------------------------------------------------------------- 1 | import { EnvironmentInjector, runInInjectionContext } from '@angular/core'; 2 | 3 | export const asyncRunInInjectionContext = ( 4 | injector: EnvironmentInjector, 5 | fn: () => Promise, 6 | ) => { 7 | return new Promise((resolve, reject) => { 8 | runInInjectionContext(injector, () => { 9 | fn() 10 | .then((value) => { 11 | resolve(value); 12 | }) 13 | .catch((error) => { 14 | reject(error); 15 | }); 16 | }); 17 | }); 18 | }; 19 | 20 | export type RunInInjectionContextProps = T & { 21 | injector: EnvironmentInjector; 22 | }; 23 | -------------------------------------------------------------------------------- /website/static/css/codeblock.css: -------------------------------------------------------------------------------- 1 | /* "Copy" code block button */ 2 | pre { 3 | position: relative; 4 | } 5 | 6 | pre .btnIcon { 7 | position: absolute; 8 | top: 4px; 9 | z-index: 2; 10 | cursor: pointer; 11 | border: 1px solid transparent; 12 | padding: 0; 13 | color: #fff; 14 | background-color: transparent; 15 | height: 30px; 16 | transition: all 0.25s ease-out; 17 | } 18 | 19 | pre .btnIcon:hover { 20 | text-decoration: none; 21 | } 22 | 23 | .btnIcon__body { 24 | align-items: center; 25 | display: flex; 26 | } 27 | 28 | .btnIcon svg { 29 | fill: currentColor; 30 | margin-right: 0.4em; 31 | } 32 | 33 | .btnIcon__label { 34 | font-size: 11px; 35 | } 36 | 37 | .btnClipboard { 38 | right: 10px; 39 | } 40 | -------------------------------------------------------------------------------- /projects/angular-redux/schematics-core/utility/package.ts: -------------------------------------------------------------------------------- 1 | import { Tree } from '@angular-devkit/schematics'; 2 | 3 | /** 4 | * Adds a package to the package.json 5 | */ 6 | export function addPackageToPackageJson( 7 | host: Tree, 8 | type: string, 9 | pkg: string, 10 | version: string, 11 | ): Tree { 12 | if (host.exists('package.json')) { 13 | const sourceText = host.read('package.json')?.toString('utf-8') ?? '{}'; 14 | const json = JSON.parse(sourceText); 15 | if (!json[type]) { 16 | json[type] = {}; 17 | } 18 | 19 | if (!json[type][pkg]) { 20 | json[type][pkg] = version; 21 | } 22 | 23 | host.overwrite('package.json', JSON.stringify(json, null, 2)); 24 | } 25 | 26 | return host; 27 | } 28 | -------------------------------------------------------------------------------- /projects/angular-redux-simple-demo/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { injectSelector, injectDispatch } from '@reduxjs/angular-redux'; 3 | import { decrement, increment } from './store/counter-slice'; 4 | import { RootState } from './store'; 5 | 6 | @Component({ 7 | selector: 'app-root', 8 | standalone: true, 9 | template: ` 10 | 11 | {{ count() }} 12 | 13 | `, 14 | }) 15 | export class AppComponent { 16 | count = injectSelector((state: RootState) => state.counter.value); 17 | dispatch = injectDispatch(); 18 | increment = increment; 19 | decrement = decrement; 20 | } 21 | -------------------------------------------------------------------------------- /projects/angular-redux/tsconfig.schematics.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "rootDir": ".", 5 | "stripInternal": true, 6 | "experimentalDecorators": true, 7 | "moduleResolution": "node", 8 | "downlevelIteration": true, 9 | "outDir": "../../dist/angular-redux", 10 | "sourceMap": true, 11 | "inlineSources": true, 12 | "lib": ["es2018", "dom"], 13 | "skipLibCheck": true, 14 | "strict": true 15 | }, 16 | "include": [ 17 | "migrations/**/*.ts", 18 | "schematics/**/*.ts", 19 | "schematics-core/**/*.ts" 20 | ], 21 | "exclude": ["**/*.spec.ts"], 22 | "angularCompilerOptions": { 23 | "skipMetadataEmit": true, 24 | "enableSummariesForJit": false, 25 | "enableIvy": false 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "website", 3 | "private": true, 4 | "scripts": { 5 | "docusaurus": "docusaurus", 6 | "start": "docusaurus start", 7 | "build": "docusaurus build", 8 | "swizzle": "docusaurus swizzle", 9 | "deploy": "docusaurus deploy", 10 | "version": "docusaurus docs:version", 11 | "serve": "docusaurus serve" 12 | }, 13 | "dependencies": { 14 | "@dipakparmar/docusaurus-plugin-umami": "^2.0.6", 15 | "@docusaurus/core": "2.4.1", 16 | "@docusaurus/preset-classic": "2.4.1", 17 | "classnames": "^2.2.6", 18 | "react": "^17.0.2", 19 | "react-dom": "^17.0.2", 20 | "react-lite-youtube-embed": "^2.0.3", 21 | "url-search-params-polyfill": "^8.1.0" 22 | }, 23 | "devDependencies": { 24 | "netlify-plugin-cache": "^1.0.3" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /website/static/css/404.css: -------------------------------------------------------------------------------- 1 | .error-page .error-message-container { 2 | margin-left: auto; 3 | margin-right: auto; 4 | padding-top: 40px; 5 | max-width: 1400px; 6 | width: 87%; 7 | } 8 | .error-page .error-message { 9 | min-height: 100vh; 10 | background: var(--ifm-blockquote-color); 11 | } 12 | .error-page .error-message span { 13 | color: var(--ifm-color-primary); 14 | font-size: 8.8em; 15 | font-weight: 700; 16 | display: inline-block; 17 | margin-top: 10vh; 18 | text-align: center; 19 | display: block; 20 | } 21 | .error-page .error-message p { 22 | margin-top: 50px; 23 | font-size: 1.6em; 24 | text-align: center; 25 | } 26 | 27 | .error-page .error-message a { 28 | margin-bottom: 50px; 29 | font-size: 1.6em; 30 | text-align: center; 31 | display: block; 32 | text-decoration: underline; 33 | } 34 | -------------------------------------------------------------------------------- /projects/angular-redux/schematics/ng-add/files/__storePath__/counter-slice.ts.template: -------------------------------------------------------------------------------- 1 | import { createSlice } from '@reduxjs/toolkit' 2 | import type { PayloadAction } from '@reduxjs/toolkit' 3 | 4 | export interface CounterState { 5 | value: number 6 | } 7 | 8 | const initialState: CounterState = { 9 | value: 0, 10 | } 11 | 12 | export const counterSlice = createSlice({ 13 | name: 'counter', 14 | initialState, 15 | reducers: { 16 | increment: (state) => { 17 | state.value += 1 18 | }, 19 | decrement: (state) => { 20 | state.value -= 1 21 | }, 22 | incrementByAmount: (state, action: PayloadAction) => { 23 | state.value += action.payload 24 | }, 25 | }, 26 | }) 27 | 28 | export const { increment, decrement, incrementByAmount } = counterSlice.actions 29 | 30 | export default counterSlice.reducer 31 | -------------------------------------------------------------------------------- /projects/angular-redux/schematics-core/testing/create-reducers.ts: -------------------------------------------------------------------------------- 1 | import { UnitTestTree } from '@angular-devkit/schematics/testing'; 2 | 3 | export function createReducers( 4 | tree: UnitTestTree, 5 | path?: string, 6 | project = 'bar', 7 | ) { 8 | tree.create( 9 | path || `/projects/${project}/src/app/reducers/index.ts`, 10 | ` 11 | import { isDevMode } from '@angular/core'; 12 | import { 13 | ActionReducer, 14 | ActionReducerMap, 15 | createFeatureSelector, 16 | createSelector, 17 | MetaReducer 18 | } from '@ngrx/${'store'}'; 19 | 20 | export interface State { 21 | 22 | } 23 | 24 | export const reducers: ActionReducerMap = { 25 | 26 | }; 27 | 28 | 29 | export const metaReducers: MetaReducer[] = isDevMode() ? [] : []; 30 | `, 31 | ); 32 | 33 | return tree; 34 | } 35 | -------------------------------------------------------------------------------- /projects/angular-redux-auto-dispatch-demo/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, effect } from '@angular/core'; 2 | import { injectSelector, injectDispatch } from '@reduxjs/angular-redux'; 3 | import { decrement, increment } from './store/counter-slice'; 4 | import { RootState } from './store'; 5 | 6 | @Component({ 7 | selector: 'app-root', 8 | standalone: true, 9 | template: ` 10 | 11 | {{ count() }} 12 | 13 | `, 14 | }) 15 | export class AppComponent { 16 | count = injectSelector((state: RootState) => state.counter.value); 17 | dispatch = injectDispatch(); 18 | increment = increment; 19 | decrement = decrement; 20 | 21 | _auto_increment = effect(() => { 22 | this.dispatch(increment()); 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /projects/angular-redux/src/lib/utils/shallowEqual.ts: -------------------------------------------------------------------------------- 1 | function is(x: unknown, y: unknown) { 2 | if (x === y) { 3 | return x !== 0 || y !== 0 || 1 / x === 1 / y; 4 | } else { 5 | return x !== x && y !== y; 6 | } 7 | } 8 | 9 | export function shallowEqual(objA: any, objB: any) { 10 | if (is(objA, objB)) return true; 11 | 12 | if ( 13 | typeof objA !== 'object' || 14 | objA === null || 15 | typeof objB !== 'object' || 16 | objB === null 17 | ) { 18 | return false; 19 | } 20 | 21 | const keysA = Object.keys(objA); 22 | const keysB = Object.keys(objB); 23 | 24 | if (keysA.length !== keysB.length) return false; 25 | 26 | for (let i = 0; i < keysA.length; i++) { 27 | if ( 28 | !Object.prototype.hasOwnProperty.call(objB, keysA[i]) || 29 | !is(objA[keysA[i]], objB[keysA[i]]) 30 | ) { 31 | return false; 32 | } 33 | } 34 | 35 | return true; 36 | } 37 | -------------------------------------------------------------------------------- /projects/angular-redux/src/tests/inject-dispatch.spec.ts: -------------------------------------------------------------------------------- 1 | import { createStore } from 'redux'; 2 | import { injectDispatch, provideRedux } from '../public-api'; 3 | import { Component } from '@angular/core'; 4 | import { render } from '@testing-library/angular'; 5 | 6 | const store = createStore((c: number = 1): number => c + 1); 7 | const store2 = createStore((c: number = 1): number => c + 2); 8 | 9 | describe('injectDispatch', () => { 10 | it("returns the store's dispatch function", async () => { 11 | @Component({ 12 | selector: 'app-root', 13 | standalone: true, 14 | template: '

', 15 | }) 16 | class Testing { 17 | dispatch = injectDispatch(); 18 | } 19 | 20 | const result = await render(Testing, { 21 | providers: [provideRedux({ store })], 22 | }); 23 | 24 | expect(result.fixture.componentRef.instance.dispatch).toBe(store.dispatch); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /projects/angular-redux/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@reduxjs/angular-redux", 3 | "version": "2.0.1", 4 | "keywords": [ 5 | "angular", 6 | "redux" 7 | ], 8 | "license": "MIT", 9 | "author": "Corbin Cruthcley (https://github.com/crutchcorn)", 10 | "homepage": "https://github.com/reduxjs/angular-redux", 11 | "repository": "github:reduxjs/angular-redux", 12 | "bugs": "https://github.com/reduxjs/angular-redux/issues", 13 | "peerDependencies": { 14 | "@angular/common": ">=19.0.0", 15 | "@angular/core": ">=19.0.0", 16 | "@reduxjs/toolkit": "^2.2.7", 17 | "redux": "^5.0.0" 18 | }, 19 | "peerDependenciesMeta": { 20 | "@reduxjs/toolkit": { 21 | "optional": true 22 | }, 23 | "redux": { 24 | "optional": true 25 | } 26 | }, 27 | "schematics": "./schematics/collection.json", 28 | "dependencies": { 29 | "tslib": "^2.3.0" 30 | }, 31 | "sideEffects": false 32 | } 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files. 2 | 3 | # Compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | /bazel-out 8 | 9 | # Node 10 | /node_modules 11 | npm-debug.log 12 | yarn-error.log 13 | 14 | # IDEs and editors 15 | .idea/ 16 | .project 17 | .classpath 18 | .c9/ 19 | *.launch 20 | .settings/ 21 | *.sublime-workspace 22 | 23 | # Visual Studio Code 24 | .vscode/* 25 | !.vscode/settings.json 26 | !.vscode/tasks.json 27 | !.vscode/launch.json 28 | !.vscode/extensions.json 29 | .history/* 30 | 31 | # Miscellaneous 32 | /.angular/cache 33 | .sass-cache/ 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | testem.log 38 | /typings 39 | 40 | # System files 41 | .DS_Store 42 | Thumbs.db 43 | 44 | .cache/ 45 | .yarn/cache/ 46 | website/.yarn/ 47 | .yarnrc 48 | .yarn/* 49 | !.yarn/patches 50 | !.yarn/releases 51 | !.yarn/plugins 52 | !.yarn/sdks 53 | !.yarn/versions 54 | .pnp.* 55 | *.tgz 56 | -------------------------------------------------------------------------------- /projects/angular-redux-injector-demo/src/app/app.component.ts: -------------------------------------------------------------------------------- 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 | 13 |

{{ count() }}

14 | @if (isLoading()) { 15 |

Loading...

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(); 24 | incrementByRandomNumber = () => { 25 | this.dispatch(incrementByRandomNumber({ injector: this.injector })); 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /website/static/img/redux.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /website/static/img/redux_white.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558 3 | "version": "2.0.0", 4 | "tasks": [ 5 | { 6 | "type": "npm", 7 | "script": "start", 8 | "isBackground": true, 9 | "problemMatcher": { 10 | "owner": "typescript", 11 | "pattern": "$tsc", 12 | "background": { 13 | "activeOnStart": true, 14 | "beginsPattern": { 15 | "regexp": "(.*?)" 16 | }, 17 | "endsPattern": { 18 | "regexp": "bundle generation complete" 19 | } 20 | } 21 | } 22 | }, 23 | { 24 | "type": "npm", 25 | "script": "test", 26 | "isBackground": true, 27 | "problemMatcher": { 28 | "owner": "typescript", 29 | "pattern": "$tsc", 30 | "background": { 31 | "activeOnStart": true, 32 | "beginsPattern": { 33 | "regexp": "(.*?)" 34 | }, 35 | "endsPattern": { 36 | "regexp": "bundle generation complete" 37 | } 38 | } 39 | } 40 | } 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2024-present Corbin Crutchley 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /projects/angular-redux/schematics/ng-add/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema", 3 | "$id": "SchematicsAngularRedux", 4 | "title": "Angular Redux Options Schema", 5 | "type": "object", 6 | "properties": { 7 | "skipPackageJson": { 8 | "type": "boolean", 9 | "default": false, 10 | "description": "Do not add Redux packages as dependencies to package.json (e.g., --skipPackageJson)." 11 | }, 12 | "path": { 13 | "type": "string", 14 | "format": "path", 15 | "description": "The path to create the state.", 16 | "visible": false, 17 | "$default": { 18 | "$source": "workingDirectory" 19 | } 20 | }, 21 | "project": { 22 | "type": "string", 23 | "description": "The name of the project.", 24 | "aliases": ["p"] 25 | }, 26 | "module": { 27 | "type": "string", 28 | "default": "app", 29 | "description": "Allows specification of the declaring module.", 30 | "alias": "m", 31 | "subtype": "filepath" 32 | }, 33 | "storePath": { 34 | "type": "string", 35 | "default": "store" 36 | } 37 | }, 38 | "required": [] 39 | } 40 | -------------------------------------------------------------------------------- /projects/angular-redux/src/lib/provider.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, OnDestroy } from '@angular/core'; 2 | import type { Action, Store, UnknownAction } from 'redux'; 3 | import { createSubscription } from './utils/Subscription'; 4 | 5 | @Injectable({ providedIn: null }) 6 | export class ReduxProvider< 7 | A extends Action = UnknownAction, 8 | S = unknown, 9 | > implements OnDestroy 10 | { 11 | store!: Store; 12 | subscription!: ReturnType; 13 | 14 | ngOnDestroy() { 15 | this.subscription.tryUnsubscribe(); 16 | this.subscription.onStateChange = undefined; 17 | } 18 | } 19 | 20 | // TODO: Ideally this runs in the constructor, but DI doesn't allow us to pass items to the constructor? 21 | export function createReduxProvider< 22 | A extends Action = UnknownAction, 23 | S = unknown, 24 | >(store: Store) { 25 | const provider = new ReduxProvider(); 26 | provider.store = store; 27 | const subscription = createSubscription(store); 28 | provider.subscription = subscription; 29 | subscription.onStateChange = subscription.notifyNestedSubs; 30 | subscription.trySubscribe(); 31 | 32 | return provider; 33 | } 34 | -------------------------------------------------------------------------------- /projects/angular-redux-simple-demo/src/app/store/counter-slice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice } from '@reduxjs/toolkit'; 2 | import type { PayloadAction } from '@reduxjs/toolkit'; 3 | 4 | export interface CounterState { 5 | value: number; 6 | } 7 | 8 | const initialState: CounterState = { 9 | value: 0, 10 | }; 11 | 12 | export const counterSlice = createSlice({ 13 | name: 'counter', 14 | initialState, 15 | reducers: { 16 | increment: (state) => { 17 | // Redux Toolkit allows us to write "mutating" logic in reducers. It 18 | // doesn't actually mutate the state because it uses the Immer library, 19 | // which detects changes to a "draft state" and produces a brand new 20 | // immutable state based off those changes 21 | state.value += 1; 22 | }, 23 | decrement: (state) => { 24 | state.value -= 1; 25 | }, 26 | incrementByAmount: (state, action: PayloadAction) => { 27 | state.value += action.payload; 28 | }, 29 | }, 30 | }); 31 | 32 | // Action creators are generated for each case reducer function 33 | export const { increment, decrement, incrementByAmount } = counterSlice.actions; 34 | 35 | export default counterSlice.reducer; 36 | -------------------------------------------------------------------------------- /projects/angular-redux-auto-dispatch-demo/src/app/store/counter-slice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice } from '@reduxjs/toolkit'; 2 | import type { PayloadAction } from '@reduxjs/toolkit'; 3 | 4 | export interface CounterState { 5 | value: number; 6 | } 7 | 8 | const initialState: CounterState = { 9 | value: 0, 10 | }; 11 | 12 | export const counterSlice = createSlice({ 13 | name: 'counter', 14 | initialState, 15 | reducers: { 16 | increment: (state) => { 17 | // Redux Toolkit allows us to write "mutating" logic in reducers. It 18 | // doesn't actually mutate the state because it uses the Immer library, 19 | // which detects changes to a "draft state" and produces a brand new 20 | // immutable state based off those changes 21 | state.value += 1; 22 | }, 23 | decrement: (state) => { 24 | state.value -= 1; 25 | }, 26 | incrementByAmount: (state, action: PayloadAction) => { 27 | state.value += action.payload; 28 | }, 29 | }, 30 | }); 31 | 32 | // Action creators are generated for each case reducer function 33 | export const { increment, decrement, incrementByAmount } = counterSlice.actions; 34 | 35 | export default counterSlice.reducer; 36 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ 2 | /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ 3 | { 4 | "compileOnSave": false, 5 | "compilerOptions": { 6 | "paths": { 7 | "@reduxjs/angular-redux": ["./dist/angular-redux"] 8 | }, 9 | "outDir": "./dist/out-tsc", 10 | "strict": true, 11 | "noImplicitOverride": true, 12 | "noPropertyAccessFromIndexSignature": true, 13 | "noImplicitReturns": true, 14 | "noFallthroughCasesInSwitch": true, 15 | "skipLibCheck": true, 16 | "isolatedModules": true, 17 | "esModuleInterop": true, 18 | "sourceMap": true, 19 | "declaration": false, 20 | "experimentalDecorators": true, 21 | "moduleResolution": "bundler", 22 | "importHelpers": true, 23 | "target": "ES2022", 24 | "module": "ES2022", 25 | "lib": ["ES2022", "dom"], 26 | "types": ["jest"] 27 | }, 28 | "angularCompilerOptions": { 29 | "enableI18nLegacyMessageIdFormat": false, 30 | "strictInjectionParameters": true, 31 | "strictInputAccessModifiers": true, 32 | "strictTemplates": true 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /projects/angular-redux-injector-demo/src/app/store/counter-slice.ts: -------------------------------------------------------------------------------- 1 | import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; 2 | import { inject } from '@angular/core'; 3 | import { RandomNumberService } from '../services/random-number.service'; 4 | import { 5 | asyncRunInInjectionContext, 6 | RunInInjectionContextProps, 7 | } from '../utils/async-run-in-injection-context'; 8 | 9 | export const incrementByRandomNumber = createAsyncThunk( 10 | 'counter/incrementByAmountFromService', 11 | (arg: RunInInjectionContextProps<{}>, _thunkAPI) => { 12 | return asyncRunInInjectionContext(arg.injector, async () => { 13 | const service = inject(RandomNumberService); 14 | const newCount = await service.getRandomNumber(); 15 | return newCount; 16 | }); 17 | }, 18 | ); 19 | 20 | export const counterSlice = createSlice({ 21 | name: 'counter', 22 | initialState: { 23 | value: 0, 24 | isLoading: false, 25 | }, 26 | reducers: {}, 27 | extraReducers: (builder) => { 28 | builder.addCase(incrementByRandomNumber.fulfilled, (state, action) => { 29 | state.isLoading = false; 30 | state.value += action.payload; 31 | }); 32 | builder.addCase(incrementByRandomNumber.pending, (state) => { 33 | state.isLoading = true; 34 | }); 35 | }, 36 | }); 37 | 38 | export default counterSlice.reducer; 39 | -------------------------------------------------------------------------------- /website/static/scripts/monokaiTheme.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plain: { 3 | color: "#f8f8f2", 4 | backgroundColor: "#272822", 5 | }, 6 | styles: [ 7 | { 8 | types: ["comment", "prolog", "doctype", "cdata"], 9 | style: { 10 | color: "#778090", 11 | }, 12 | }, 13 | { 14 | types: ["punctuation"], 15 | style: { 16 | color: "#F8F8F2", 17 | }, 18 | }, 19 | { 20 | types: ["property", "tag", "constant", "symbol", "deleted"], 21 | style: { 22 | color: "#F92672", 23 | }, 24 | }, 25 | { 26 | types: ["boolean", "number"], 27 | style: { 28 | color: "#AE81FF", 29 | }, 30 | }, 31 | { 32 | types: ["selector", "attr-name", "string", "char", "builtin", "inserted"], 33 | style: { 34 | color: "#a6e22e", 35 | }, 36 | }, 37 | { 38 | types: ["operator", "entity", "url", "variable"], 39 | style: { 40 | color: "#F8F8F2", 41 | }, 42 | }, 43 | { 44 | types: ["atrule", "attr-value", "function"], 45 | style: { 46 | color: "#E6D874", 47 | }, 48 | }, 49 | { 50 | types: ["keyword"], 51 | style: { 52 | color: "#F92672", 53 | }, 54 | }, 55 | { 56 | types: ["regex", "important"], 57 | style: { 58 | color: "#FD971F", 59 | }, 60 | }, 61 | ], 62 | }; 63 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. 4 | 5 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion. 6 | 7 | Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. 8 | 9 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. 10 | 11 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. 12 | 13 | This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/) 14 | -------------------------------------------------------------------------------- /website/static/img/github-brands.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /projects/angular-redux/schematics-core/testing/create-app-module.ts: -------------------------------------------------------------------------------- 1 | import { UnitTestTree } from '@angular-devkit/schematics/testing'; 2 | 3 | export function createAppModule( 4 | tree: UnitTestTree, 5 | path?: string, 6 | ): UnitTestTree { 7 | tree.create( 8 | path || '/src/app/app.module.ts', 9 | ` 10 | import { BrowserModule } from '@angular/platform-browser'; 11 | import { NgModule } from '@angular/core'; 12 | import { AppComponent } from './app.component'; 13 | 14 | @NgModule({ 15 | declarations: [ 16 | AppComponent 17 | ], 18 | imports: [ 19 | BrowserModule 20 | ], 21 | providers: [], 22 | bootstrap: [AppComponent] 23 | }) 24 | export class AppModule { } 25 | `, 26 | ); 27 | 28 | return tree; 29 | } 30 | 31 | export function createAppModuleWithEffects( 32 | tree: UnitTestTree, 33 | path: string, 34 | effects?: string, 35 | ): UnitTestTree { 36 | tree.create( 37 | path || '/src/app/app.module.ts', 38 | ` 39 | import { BrowserModule } from '@angular/platform-browser'; 40 | import { NgModule } from '@angular/core'; 41 | import { AppComponent } from './app.component'; 42 | import { EffectsModule } from '@ngrx/effects'; 43 | 44 | @NgModule({ 45 | declarations: [ 46 | AppComponent 47 | ], 48 | imports: [ 49 | BrowserModule, 50 | ${effects} 51 | ], 52 | providers: [], 53 | bootstrap: [AppComponent] 54 | }) 55 | export class AppModule { } 56 | `, 57 | ); 58 | 59 | return tree; 60 | } 61 | -------------------------------------------------------------------------------- /website/src/pages/styles.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 4rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | 13 | @media screen and (max-width: 966px) { 14 | .heroBanner { 15 | padding: 2rem; 16 | } 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | 25 | .features { 26 | display: flex; 27 | align-items: center; 28 | padding: 2rem 0; 29 | width: 100%; 30 | } 31 | 32 | .featureBlock { 33 | margin-top: 40px; 34 | } 35 | 36 | .blockImage { 37 | margin-bottom: 20px; 38 | margin-left: auto; 39 | margin-right: auto; 40 | max-width: 80px; 41 | } 42 | 43 | html[data-theme="dark"] .blockImage img { 44 | filter: invert(100%); 45 | } 46 | 47 | .blockImage svg { 48 | fill: currentColor; 49 | width: 60px; 50 | height: 60px; 51 | } 52 | 53 | .featureTitle { 54 | font-size: 30px; 55 | padding: 10px 0; 56 | } 57 | 58 | @media screen and (max-width: 966px) { 59 | .featureContent { 60 | text-align: center; 61 | } 62 | } 63 | 64 | .title { 65 | display: flex; 66 | justify-content: center; 67 | align-items: center; 68 | } 69 | 70 | .projectTitle { 71 | margin: 0.3em 0; 72 | } 73 | 74 | .featureAnchor svg { 75 | margin-left: 5px; 76 | max-width: 16px; 77 | max-height: 16px; 78 | fill: currentColor; 79 | } 80 | 81 | .getStarted:hover { 82 | background: #fff; 83 | color: var(--ifm-color-primary-dark); 84 | } 85 | 86 | .secondTitle { 87 | font-size: 30px; 88 | margin-bottom: 55px; 89 | } 90 | -------------------------------------------------------------------------------- /projects/angular-redux/src/tests/utils/Subscription.spec.ts: -------------------------------------------------------------------------------- 1 | import { createSubscription, Subscription } from '../../lib/utils/Subscription'; 2 | import { Store } from 'redux'; 3 | 4 | describe('Subscription', () => { 5 | let notifications: string[]; 6 | let store: Store; 7 | let parent: Subscription; 8 | 9 | beforeEach(() => { 10 | notifications = []; 11 | store = { subscribe: () => jest.fn() } as unknown as Store; 12 | 13 | parent = createSubscription(store); 14 | parent.onStateChange = () => {}; 15 | parent.trySubscribe(); 16 | }); 17 | 18 | function subscribeChild(name: string) { 19 | const child = createSubscription(store, parent); 20 | child.onStateChange = () => notifications.push(name); 21 | child.trySubscribe(); 22 | return child; 23 | } 24 | 25 | it('listeners are notified in order', () => { 26 | subscribeChild('child1'); 27 | subscribeChild('child2'); 28 | subscribeChild('child3'); 29 | subscribeChild('child4'); 30 | 31 | parent.notifyNestedSubs(); 32 | 33 | expect(notifications).toEqual(['child1', 'child2', 'child3', 'child4']); 34 | }); 35 | 36 | it('listeners can be unsubscribed', () => { 37 | const child1 = subscribeChild('child1'); 38 | const child2 = subscribeChild('child2'); 39 | const child3 = subscribeChild('child3'); 40 | 41 | child2.tryUnsubscribe(); 42 | parent.notifyNestedSubs(); 43 | 44 | expect(notifications).toEqual(['child1', 'child3']); 45 | notifications.length = 0; 46 | 47 | child1.tryUnsubscribe(); 48 | parent.notifyNestedSubs(); 49 | 50 | expect(notifications).toEqual(['child3']); 51 | notifications.length = 0; 52 | 53 | child3.tryUnsubscribe(); 54 | parent.notifyNestedSubs(); 55 | 56 | expect(notifications).toEqual([]); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /projects/angular-redux/schematics-core/testing/create-workspace.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SchematicTestRunner, 3 | UnitTestTree, 4 | } from '@angular-devkit/schematics/testing'; 5 | 6 | export const defaultWorkspaceOptions = { 7 | name: 'workspace', 8 | newProjectRoot: 'projects', 9 | version: '6.0.0', 10 | }; 11 | 12 | export const defaultAppOptions = { 13 | name: 'bar', 14 | inlineStyle: false, 15 | inlineTemplate: false, 16 | viewEncapsulation: 'Emulated', 17 | routing: false, 18 | style: 'css', 19 | skipTests: false, 20 | standalone: false, 21 | }; 22 | 23 | const defaultLibOptions = { 24 | name: 'baz', 25 | }; 26 | 27 | export function getTestProjectPath( 28 | workspaceOptions: any = defaultWorkspaceOptions, 29 | appOptions: any = defaultAppOptions, 30 | ) { 31 | return `/${workspaceOptions.newProjectRoot}/${appOptions.name}`; 32 | } 33 | 34 | export async function createWorkspace( 35 | schematicRunner: SchematicTestRunner, 36 | appTree: UnitTestTree, 37 | workspaceOptions = defaultWorkspaceOptions, 38 | appOptions = defaultAppOptions, 39 | libOptions = defaultLibOptions, 40 | ) { 41 | appTree = await schematicRunner.runExternalSchematic( 42 | '@schematics/angular', 43 | 'workspace', 44 | workspaceOptions, 45 | ); 46 | 47 | appTree = await schematicRunner.runExternalSchematic( 48 | '@schematics/angular', 49 | 'application', 50 | appOptions, 51 | appTree, 52 | ); 53 | 54 | appTree = await schematicRunner.runExternalSchematic( 55 | '@schematics/angular', 56 | 'application', 57 | { ...appOptions, name: 'bar-standalone', standalone: true }, 58 | appTree, 59 | ); 60 | 61 | appTree = await schematicRunner.runExternalSchematic( 62 | '@schematics/angular', 63 | 'library', 64 | libOptions, 65 | appTree, 66 | ); 67 | 68 | return appTree; 69 | } 70 | -------------------------------------------------------------------------------- /website/src/theme/NotFound.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | import React, { useEffect } from "react"; 8 | import Layout from "@theme/Layout"; 9 | import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; 10 | 11 | function NotFound() { 12 | const context = useDocusaurusContext(); 13 | const { siteConfig = {} } = context; 14 | useEffect(() => { 15 | const script = document.createElement("script"); 16 | script.src = "https://buttons.github.io/buttons.js"; 17 | script.async = true; 18 | document.body.appendChild(script); 19 | }, []); 20 | 21 | const getTrackingScript = () => { 22 | if ( 23 | !siteConfig.themeConfig || 24 | !siteConfig.themeConfig.googleAnalytics || 25 | !siteConfig.themeConfig.googleAnalytics.gaTrackingId 26 | ) { 27 | return null; 28 | } 29 | 30 | return { 31 | __html: ` 32 | ga('create', "${siteConfig.gaTrackingId}"); 33 | ga('send', { 34 | hitType: 'event', 35 | eventCategory: '404 Response', 36 | eventAction: window.location.href, 37 | eventLabel: document.referrer 38 | });`, 39 | }; 40 | }; 41 | const trackingScript = getTrackingScript(); 42 | 43 | return ( 44 | 45 | {trackingScript &&