{"id":21206300,"url":"https://github.com/sara-pixie/angular-digital-welcome-wallets","last_synced_at":"2025-03-14T23:12:05.928Z","repository":{"id":189664073,"uuid":"468702220","full_name":"Sara-pixie/angular-digital-welcome-wallets","owner":"Sara-pixie","description":null,"archived":false,"fork":false,"pushed_at":"2022-03-17T18:48:15.000Z","size":489,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-21T15:32:30.703Z","etag":null,"topics":["angular","firebase","form","ngrx-actions","ngrx-effects","ngrx-reducers","ngrx-selectors","ngrx-store","reactive-forms","table"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Sara-pixie.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-03-11T10:17:19.000Z","updated_at":"2022-03-16T11:02:54.000Z","dependencies_parsed_at":null,"dependency_job_id":"d2d1e02f-7aa6-453e-bfd1-ba1308423f27","html_url":"https://github.com/Sara-pixie/angular-digital-welcome-wallets","commit_stats":null,"previous_names":["sara-pixie/angular-digital-welcome-wallets"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sara-pixie%2Fangular-digital-welcome-wallets","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sara-pixie%2Fangular-digital-welcome-wallets/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sara-pixie%2Fangular-digital-welcome-wallets/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sara-pixie%2Fangular-digital-welcome-wallets/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Sara-pixie","download_url":"https://codeload.github.com/Sara-pixie/angular-digital-welcome-wallets/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243658275,"owners_count":20326467,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","firebase","form","ngrx-actions","ngrx-effects","ngrx-reducers","ngrx-selectors","ngrx-store","reactive-forms","table"],"created_at":"2024-11-20T20:55:13.912Z","updated_at":"2025-03-14T23:12:05.922Z","avatar_url":"https://github.com/Sara-pixie.png","language":"TypeScript","readme":"# Angular Digital Welcome Wallets\n\nThis project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 13.1.2.\n\n# Table of Contents\n\n- **Firebese**\n  - [Connecting Angular app to Firebase](#connecting-angular-app-to-firebase)\n  - [How to get data from Fidebase DB](#how-to-get-data-from-fidebase-db)\n  - [How to push data to Firestore DB](#how-to-push-data-to-firestore-db)\n- [NgRx Store](#ngrx-store)\n  - [Create an ACTION](#create-an-action)\n  - [Define AppState](#define-appstate)\n  - [Create a REDUCER](#create-a-reducer)\n  - [Creating EFFECTS (HTTP)](#creating-effects-http)\n  - [Creating SELECTORS](#creating-selectors)\n- [Added Dependencies](#added-dependencies)\n\n## Connecting Angular app to Firebase\n\n1. create [Firebase](https://console.firebase.google.com/) project\n\n2. create a realtime database\n   (useful info - [rules](https://firebase.google.com/docs/database/security/rules-conditions))\n\n3. install packages in your project:\n   npm i firebase @angular/fire --save\n   (will also probably need to -\u003e npm audit fix)\n\n4. go to main page of your project in Firebase \u0026 click add WEB app\n\n5. find Your web app's Firebase configuration info and copy it to both environments!\n\n```\n    firebaseConfig: {\n        apiKey: \"AIzaSyDKqRHuxd8a4GlD-oi20UE0Hlr4rz3wl5Y\",\n        authDomain: \"angular-digital-welcome-wallet.firebaseapp.com\",\n        databaseURL: \"https://angular-digital-welcome-wallet-default-rtdb.europe-west1.firebasedatabase.app\",\n        projectId: \"angular-digital-welcome-wallet\",\n        storageBucket: \"angular-digital-welcome-wallet.appspot.com\",\n        messagingSenderId: \"573177592731\",\n        appId: \"1:573177592731:web:fdbfa1c7ebf948ffbcff3e\"\n    }\n```\n\n6. in AppModule imports array add:\n   [CRUD example setup](https://www.bezkoder.com/angular-13-firebase-crud/)\n\n```\n    import { AngularFireModule } from '@angular/fire/compat';\n    import { AngularFireDatabaseModule } from '@angular/fire/compat/database';\n    import { environment } from 'src/environments/environment';\n    ...\n    AngularFireModule.initializeApp(environment.firebaseConfig),\n    AngularFireDatabaseModule\n```\n\n\u003cbr\u003e(-\u003eBack To [Table of Contents](#table-of-contents))\n\n## How to get data from Fidebase DB\n\nhttps://firebase.google.com/docs/database/web/read-and-write\nin component import:\n\n```\n    import { AngularFireDatabase } from '@angular/fire/compat/database';\n\n```\n\nin component constructor:\n\n```\n    constructor(db: AngularFireDatabase){\n        db.list\u003cStore\u003e('STORE').valueChanges().subscribe((stores: Store[]) =\u003e {\n            this.stores = stores;\n        });\n        db.list\u003cForm\u003e('FORM').valueChanges().subscribe((forms: Form[]) =\u003e {\n            this.forms = forms;\n        });\n    }\n```\n\nthen in html:\n\n```\n    ...\n    *ngFor=\"let store of stores\"\u003e\n    {{ store.name }}\n    ...\n```\n\n\u003cbr\u003e(-\u003eBack To [Table of Contents](#table-of-contents))\n\n## How to push data to Firestore DB\n\nin component method:\n\n```\n    db.list('FORM').push(newFormInfo);\n```\n\n\u003cbr\u003e(-\u003eBack To [Table of Contents](#table-of-contents))\n\n## NgRx Store\n\nhttps://coursetro.com/posts/code/151/Angular-Ngrx-Store-Tutorial---Learn-Angular-State-Management\n\n**npm install @ngrx/store**\n\n\u003cbr\u003e(-\u003eBack To [Table of Contents](#table-of-contents))\n\n### Create an ACTION\n\nAn action in Ngrx/store is two things:\n\n- A type in the form of a string. It describes what's happening.\n- It contains an optional payload of data.\n\n```\n    // Define the type of action (which is in the form of a string constant)\n    export const GET_STORES = 'GetStores';\n    ...\n\n    // Create a class for each action with a constructor that allows us to pass in the payload (not a required step, but it does provide you with strong typing)\n    export class AddFormInfo implements Action {\n        readonly type: string = ADD_FORM_INFO;\n        constructor(public payload: FormInfo){}\n    }\n    ...\n\n    // We're exporting all of our action classes for use within our reducer\n    export type Actions =\n        GetStoresStart |\n        GetStores |\n        GetFormsInfoStart |\n        GetFormsInfo |\n        AddFormInfo;\n```\n\n\u003cbr\u003e(-\u003eBack To [Table of Contents](#table-of-contents))\n\n### Define AppState\n\n```\nexport interface AppState {\n  readonly stores: Store[];\n  readonly allFormsInfo: FormInfo[];\n}\n\n```\n\n\u003cbr\u003e(-\u003eBack To [Table of Contents](#table-of-contents))\n\n### Create a REDUCER\n\nA reducer is what takes the incoming action and decides what to do with it. It takes the previous state and returns a new state based on the given action.\n\n```\n    // First import everything from your actions file and asign it an alias\n    import * as AppActions from './app.actions';\n```\n\nThen define your reducer. \u003cbr\u003e\nIt's a function that accepts 2 arguments: 1. **state** (which in this case is an array of FormInfo and has an assigned default of [] - that can be something else as well...) 2. **action** (make it dynamic by getting the exported types in your actions!)\ninside it make a **switch** function that checks the type of action being passed in (it comares them to those dynamic actions we referenced earlier) \u003cbr\u003e\nThen specify what should happen in each case (there can be more logic before the return statement) and make sure the default option return the initial state (so that's why we have state: FormInfo[] = []).\n\n```\n    export function formInfoReducer(state: FormInfo[] = [], action: AppActions.Actions){\n    switch(action.type){\n        case AppActions.ADD_FORM_INFO:\n            return [...state, (action as AppActions.AddFormInfo).payload];\n        case AppActions.GET_FORMS_INFO:\n            return {...state};\n        default:\n            return state;\n    }\n}\n```\n\nconnect a reducer (app.module.ts):\n\n```\n    import { StoreModule } from '@ngrx/store';\n    import { storesReducer } from './shared/store/stores.reducer';\n    import { formInfoReducer } from './shared/store/formInfo.reducer';\n    ...\n    @NgModule({\n        ...\n        imports: [\n            StoreModule.forRoot({\n                // key - same as in AppState!\n                // value - reducer name\n                stores: storesReducer,\n                allFormsInfo: formInfoReducer\n            })\n        ]\n        ...\n```\n\nUse the Reducer:\n\n```\n    import { Store } from '@ngrx/store';\n    import * as AppActions from '../../shared/store/app.actions';\n    ...\n    constructor(private store: Store\u003cAppState\u003e) {}\n    ...\n\n    // send info (DISPATCH)\n    // make sure to call 'new' AppActions.actionFunctionName(payload) !\n    const formInfo: FormInfo = { /*FormInfo object here*/ }\n    this.store.dispatch(new AppActions.AddFormInfo(formInfo));\n\n    ...\n\n    // get state info\n    // will create an Observable\u003ctype\u003e\n    // call the store and then '.select' the part of AppState you're interested in - also see app.module.ts!\n    allFormsInfo: Observable\u003cFormInfo[]\u003e;\n    constructor(store: Store\u003cAppState\u003e) {\n        this.allFormsInfo = store.select('allFormsInfo');\n    }\n\n```\n\n\u003cbr\u003e(-\u003eBack To [Table of Contents](#table-of-contents))\n\n### Creating EFFECTS (HTTP)\n\n**npm install --save @ngrx/effects**\n\nin name.effects.ts:\n\n```\n    import { Actions, createEffect, ofType } from '@ngrx/effects';\n    import { catchError, map, switchMap } from 'rxjs';\n    import { AngularFireDatabase } from '@angular/fire/compat/database';\n    import * as AppActions from './app.actions';\n    ...\n    @Injectable() // so we can inject actions$ and db\n    export class AppEffects{\n        getAllStores = createEffect(() =\u003e {\n            return this.actions$.pipe(\n                // continue here only if the action(Observable) is of this type\n                // can add multiple actions if you want to run the same code for them!\n                // it's like a filter...\n                ofType(AppActions.GET_STORES_START),\n                // get new observable\n                switchMap(() =\u003e{\n                    //get array of stores\n                    return this.db.list\u003cStore\u003e('STORE').valueChanges()\n                        // and trigger another action to save them to NgRx Store\n                        .pipe(\n                            map((stores: Store[]) =\u003e new AppActions.GetStores(stores)),\n                            // must return a non error observable so actions$ observable doesn't die!\n                            catchError(() =\u003e new AppActions.GetStoresError())\n                        );\n                })\n            )\n        });\n        ...more effects...\n        constructor(private actions$: Actions,\n                    private db: AngularFireDatabase){}\n    }\n```\n\nin app.module.ts:\n\n```\n    import { EffectsModule } from '@ngrx/effects';\n    ...\n    imports: [\n        EffectsModule.forRoot([AppEffects])\n    ]\n    ...\n```\n\n**New Flow:**\n\n1. `this.ngRxStore.dispatch(new AppActions.GetStoresStart());` in component\n2. because `GetStoresStart()` is 'registered in reducers it triggers the code for \"getAllStores\" effect\n3. \"getAllStores\" effect makes a call to DB to get stores info\n4. then it calls eithar `GetStoresError()` action or `GetStores(stores)` action\n5. `GetStores(stores)` action triggers a reducer which updated the \"AppState\":\n\n```\n    export function storesReducer(state: Store[] = [], action: AppActions.Actions){\n        switch(action.type){\n            case AppActions.GET_STORES:\n                return [...state, ...(action as AppActions.GetStores).payload];\n            default:\n                return state;\n        }\n    }\n```\n\n6. Then you can get the updated state from the component `this.stores = ngRxStore.select('stores');` as an observable\n\n\u003cbr\u003e(-\u003eBack To [Table of Contents](#table-of-contents))\n\n### Creating SELECTORS\n\nWhy Selectors?\n\n1. **Memoization** First of all, selectors created by `createSelector` are memorized, meaning that they won't be called unnecessarily unless the data in the store has changed. This provides an important performance boost\n2. **Easy composition** In functional programming, we can compose different simple, pure functions into more complex ones, thus making the code way more readable and the whole system more maintainable\n3. **Cleanliness** We can always easily find from where a particular state is coming, and debug/find issues with no hassle\n4. **Consistency** It is always a good idea to do certain things in a single, concise way\n\nThere are some rules when creating selectors and states:\n\n1. Selectors must be pure functions\n2. Never keep derived state in a store\n3. Use the tools provided by NgRx, like\n\n- `createFeatureSelector` (allows us to get a top-level feature state property of the state tree simply by calling it out by its feature name)\n\n```\n    const storesFeature = createFeatureSelector\u003cStore[]\u003e('stores');\n    const formsInfoFeature = createFeatureSelector\u003cFormInfo[]\u003e('allFormsInfo');\n```\n\n- `createSelector` (can be used to select some data from the state based on several slices of the same state)\n\n```\n    export const allStores = createSelector(storesFeature, (state: Store[]) =\u003e state);\n    export const allFormsInfo = createSelector(formsInfoFeature, (state: FormInfo[]) =\u003e state);\n    export const oneStoreInfo = (index: number) =\u003e createSelector(storesFeature, (state: Store[]) =\u003e {\n        if(state.length \u003e 0){\n            return state[index];\n        } else {\n            return null;\n        }\n    });\n```\n\n- and `Entity.getSelectors`\n\n4. Selectors should be short and do one thing for one result. If two parts of an app use some state in two different forms, don't try to bastardize the selector, but either create two selectors or create the next selector from the first one using `createSelector`. Another approach is to write selector factories.\n5. Try to always use named selectors\n\n**Use selectors**\n\n```\n    import * as Selectors from '../../shared/store/app.selectors';\n    ...\n    ngRxStore.select(Selectors.allStores).subscribe((stores) =\u003e console.log(\"All Stores:\", stores));\n    ngRxStore.select(Selectors.allFormsInfo).subscribe((forms) =\u003e console.log(\"All FormsInfo:\", forms));\n    ngRxStore.select(Selectors.oneStoreInfo(0)).subscribe((store) =\u003e console.log(\"First store:\", store));\n```\n\n\u003cbr\u003e(-\u003eBack To [Table of Contents](#table-of-contents))\n\n## Added Dependencies\n\n- @angular/fire: ^7.2.1\n- firebase: ^9.6.8\n- @ngrx/store: ^13.0.2\n- @ngrx/effects: ^13.0.2\n- @fortawesome/angular-fontawesome: ^0.10.1\n- @fortawesome/fontawesome-svg-core: ^6.1.0\n- @fortawesome/free-solid-svg-icons: ^6.1.0\n\n\u003cbr\u003e(-\u003eBack To [Table of Contents](#table-of-contents))\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsara-pixie%2Fangular-digital-welcome-wallets","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsara-pixie%2Fangular-digital-welcome-wallets","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsara-pixie%2Fangular-digital-welcome-wallets/lists"}