diff --git a/.travis.yml b/.travis.yml
index 412c732a4..229664fbd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -15,4 +15,4 @@ branches:
install: yarn
-script: yarn build && yarn test:all
+script: yarn build && yarn test:all && yarn lint
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 81142b24b..f1c0434ed 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,40 @@
+
+# [6.0.1](https://github.com/angular/angularfire2/compare/6.0.0...6.0.1) (2020-06-24)
+
+* Updating peer dependencies to allow for Angular 10
+* `ng add @angular/fire` should correctly add the `firebase` peer
+* `ng add @angular/fire` will not duplicate settings entries, if they're already present
+* `ng add @angular/fire` will error if there are peer incompatabilities
+* `ng deploy` should function correctly on Windows devices
+* `ng deploy` will now mark the Angular assets as immutable on Firebase Hosting
+* RTDB and Firestore CRUD operations should return in the ngZone
+* Use of `AngularFireAuthGuard` should no longer destablize Zone.js
+
+
+# [6.0.0](https://github.com/angular/angularfire2/compare/6.0.0-rc.2...6.0.0) (2020-04-01)
+
+Final relase of version 6.
+
+* Updating peer dependencies
+* `ng add @angular/fire` now will overwrite firebase config, if present
+* `ng add @angular/fire` now adds `@firebase/firestore` to the server schematic `externalDependencies` if present
+* `ng deploy --preview` is now interactive and functions on non-SSR
+* `ng deploy` will respect the `bundleDependencies` and `externalDependencies` server schematic options
+* `ng deploy` now defaults to 1GB of ram on Cloud Functions
+* Fixed various issues with functions deploy
+* Simplified `AngularPerformanceMonitoring`
+
+
+# [6.0.0-rc.2](https://github.com/angular/angularfire2/compare/6.0.0-rc.1...6.0.0-rc.2) (2020-03-29)
+
+Continued work on 6.0.
+
+* Update peers
+* No longer require developer add bare imports themselves (e.g, `import 'firebase/firestore'`)
+* Zone.js fixes
+* Angular Universal deploy schematic
+* Storage listAll()
+
# [6.0.0-rc.1](https://github.com/angular/angularfire2/compare/6.0.0-rc.0...6.0.0-rc.1) (2020-02-06)
@@ -74,7 +111,7 @@ Version 6 of AngularFire drops support for Angular version 8 and below, older ve
* Dropped `StorageBucket` DI token in favor of `BUCKET`
-## [5.4.2](https://github.com/angular/angularfire2/compare/5.4.1...5.4.2) (2020-02-06)
+# [5.4.2](https://github.com/angular/angularfire2/compare/5.4.1...5.4.2) (2020-02-06)
### Bug Fixes
@@ -82,7 +119,7 @@ Version 6 of AngularFire drops support for Angular version 8 and below, older ve
-## [5.4.1](https://github.com/angular/angularfire2/compare/5.4.0...5.4.1) (2020-02-05)
+# [5.4.1](https://github.com/angular/angularfire2/compare/5.4.0...5.4.1) (2020-02-05)
### Bug Fixes
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 47484db41..de9c0e29e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -80,8 +80,8 @@ Providing the following information will increase the chances of your issue bein
* **Angular Version** - what version of Angular, Firebase, and AngularFire are you using?
* **Motivation for or Use Case** - explain what are you trying to do and why the current behavior is a bug for you
* **Browsers and Operating System** - is this a problem with all browsers?
-* **Reproduce the Error** - provide a live example (using [Plunker][plunker],
- [JSFiddle][jsfiddle] or [Runnable][runnable]) or a unambiguous set of steps
+* **Reproduce the Error** - provide a live example (using StackBlitz (https://stackblitz.com/edit/angular-fire-start))
+ or a unambiguous set of steps
* **Related Issues** - has a similar issue been reported before?
* **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be
causing the problem (line of code or commit)
diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md
index 0f6a4eb5e..6b7814848 100644
--- a/ISSUE_TEMPLATE.md
+++ b/ISSUE_TEMPLATE.md
@@ -46,12 +46,12 @@ be fixed in the latest versions. -->
### How to reproduce these conditions
-**Failing test unit, Plunkr, or JSFiddle demonstrating the problem**
+**Failing test unit, Stackblitz demonstrating the problem**
**Steps to set up and reproduce**
@@ -60,7 +60,7 @@ Plunker (http://plnkr.co/) or JSFiddle (https://jsfiddle.net/).
**Sample data and security rules**
-<-- include/attach/link to some json sample data (or provide credentials to a sanitized, test Firebase project) -->
+
### Debug output
diff --git a/README.md b/README.md
index 05dc39679..f8aa9ddfc 100644
--- a/README.md
+++ b/README.md
@@ -51,7 +51,7 @@ export class MyApp {
[Contributing](CONTRIBUTING.md)
-[Stackblitz Template](https://stackblitz.com/edit/angular-1iment) - Remember to set your Firebase configuration in `app/app.module.ts`.
+[Stackblitz Template](https://stackblitz.com/edit/angular-fire-start) - Remember to set your Firebase configuration in `app/app.module.ts`.
[Upgrading to v6.0? Check out our guide.](docs/version-6-upgrade.md)
diff --git a/docs/analytics/getting-started.md b/docs/analytics/getting-started.md
index 8db7a2f9d..76ee26859 100644
--- a/docs/analytics/getting-started.md
+++ b/docs/analytics/getting-started.md
@@ -122,7 +122,7 @@ See the gtag.js documentation to learn of the different configuration options at
### Use DebugView `DEBUG_MODE`
-To use [DebugView in Analtyics](https://console.firebase.google.com/project/_/analytics/debugview) set `DEBUG_MODE` to `true` (*default: false*).
+To use [DebugView in Analytics](https://console.firebase.google.com/project/_/analytics/debugview) set `DEBUG_MODE` to `true` (*default: false*).
### Track deployments with `APP_NAME` and `APP_VERSION`
@@ -132,4 +132,4 @@ If you provide `APP_NAME` and `APP_VERSION` (*default: undefined*) you will be a
If you set `COLLECTION_ENABLED` (*default: true*) to `false` then analytics collection will be disabled for this app on this device. To opt back in to analytics collection you could then call `setAnalyticsCollectionEnabled(true)`.
-Putting these APIs to use with cookies would allow you to create a flexible analytics collection scheme that would respect your user's desire for privacy.
\ No newline at end of file
+Putting these APIs to use with cookies would allow you to create a flexible analytics collection scheme that would respect your user's desire for privacy.
diff --git a/docs/auth/router-guards.md b/docs/auth/router-guards.md
index e326f68f6..841eb306b 100644
--- a/docs/auth/router-guards.md
+++ b/docs/auth/router-guards.md
@@ -39,7 +39,7 @@ const belongsToAccount = (next) => hasCustomClaim(`account-${next.params.id}`);
export const routes: Routes = [
{ path: '', component: AppComponent },
{ path: 'login', component: LoginComponent, canActivate: [AngularFireAuthGuard], data: { authGuardPipe: redirectLoggedInToItems }},
- { path: 'items', component: ItemListComponent, canActivate: [AngularFireAuthGuard], data: { authGuardPipe: redirectUnauthorizedToLogin },
+ { path: 'items', component: ItemListComponent, canActivate: [AngularFireAuthGuard], data: { authGuardPipe: redirectUnauthorizedToLogin }},
{ path: 'admin', component: AdminComponent, canActivate: [AngularFireAuthGuard], data: { authGuardPipe: adminOnly }},
{ path: 'accounts/:id', component: AdminComponent, canActivate: [AngularFireAuthGuard], data: { authGuardPipe: belongsToAccount }}
];
diff --git a/docs/deploy/getting-started.md b/docs/deploy/getting-started.md
index 1158989ae..b9fb66276 100644
--- a/docs/deploy/getting-started.md
+++ b/docs/deploy/getting-started.md
@@ -28,25 +28,25 @@ The schematics will do the following:
In the end, your `angular.json` project will look like below:
-```js
+```json5
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"sample-app": {
- // ...
- "deploy": {
- "builder": "@angular/fire:deploy",
- "options": {} // Here you may find an "ssr": true option if you've
- // selected that you want to deploy your Angular universal project
- // as a firebase function.
+ // ...
+ "deploy": {
+ "builder": "@angular/fire:deploy",
+ "options": {} // Here you may find an "ssr": true option if you've
+ // selected that you want to deploy your Angular universal project
+ // as a firebase function.
}
}
- }
- // ...
},
+ // ...
"defaultProject": "sample-app"
+
}
```
diff --git a/docs/firestore/collections.md b/docs/firestore/collections.md
index 8099f5988..adeb2cb2a 100644
--- a/docs/firestore/collections.md
+++ b/docs/firestore/collections.md
@@ -43,7 +43,7 @@ The `AngularFirestoreCollection` is a service you use to create streams of the c
With the exception of the `valueChanges()`, each streaming method returns an Observable of `DocumentChangeAction[]`.
-A `DocumentChangeAction` gives you the `type` and `payload` properties. The `type` tells when what `DocumentChangeType` operation occured (`added`, `modified`, `removed`). The `payload` property is a `DocumentChange` which provides you important metdata about the change and a `doc` property which is the `DocumentSnapshot`.
+A `DocumentChangeAction` gives you the `type` and `payload` properties. The `type` tells when what `DocumentChangeType` operation occured (`added`, `modified`, `removed`). The `payload` property is a `DocumentChange` which provides you important metadata about the change and a `doc` property which is the `DocumentSnapshot`.
```ts
interface DocumentChangeAction {
@@ -191,7 +191,7 @@ import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
export interface AccountDeposit { description: string; amount: number; }
-export interface AccountDepoistId extends AccountDeposit { id: string; }
+export interface AccountDepositId extends AccountDeposit { id: string; }
@Component({
selector: 'app-root',
diff --git a/docs/functions/functions.md b/docs/functions/functions.md
index a9713e80b..47f2f52e7 100644
--- a/docs/functions/functions.md
+++ b/docs/functions/functions.md
@@ -110,7 +110,7 @@ import { AngularFireFunctionsModule, ORIGIN } from '@angular/fire/functions';
],
...
providers: [
- { provide: ORIGIN, useValue: 'http://localhost:5005' }
+ { provide: ORIGIN, useValue: 'http://localhost:5001' }
]
})
export class AppModule {}
@@ -145,7 +145,7 @@ Next, configure functions origin to point at your app domain:
```ts
import { NgModule } from '@angular/core';
-import { AngularFireFunctionsModule, FUNCTIONS_ORIGIN } from '@angular/fire/functions';
+import { AngularFireFunctionsModule, ORIGIN } from '@angular/fire/functions';
@NgModule({
imports: [
diff --git a/docs/install-and-setup.md b/docs/install-and-setup.md
index 095b7de00..eb6483352 100644
--- a/docs/install-and-setup.md
+++ b/docs/install-and-setup.md
@@ -13,14 +13,14 @@ The Angular CLI's `new` command will set up the latest Angular build in a new pr
### 2. Install AngularFire and Firebase
```bash
-ng add @angular/fire@next
+ng add @angular/fire
```
Now that you have a new project setup, install AngularFire and Firebase from npm.
### 3. Add Firebase config to environments variable
-Open `/src/environments/environment.ts` and add your Firebase configuration. You can find your project configuration in [the Firebase Console](https://console.firebase.google.com). From the project overview page, click **Add Firebase to your web app**.
+Open `/src/environments/environment.ts` and add your Firebase configuration. You can find your project configuration in [the Firebase Console](https://console.firebase.google.com). Click the Gear icon next to Project Overview, then click Project Settings and under "Firebase SDK snippet" click Config.
```ts
export const environment = {
@@ -62,7 +62,7 @@ export class AppModule {}
After adding the AngularFireModule you also need to add modules for the individual @NgModules that your application needs.
-For example if your application was using both Google Analtyics and the Firestore you would add `AngularFireAnalyticsModule` and `AngularFirestoreModule`:
+For example if your application was using both Google Analytics and the Firestore you would add `AngularFireAnalyticsModule` and `AngularFirestoreModule`:
```ts
import { BrowserModule } from '@angular/platform-browser';
@@ -146,7 +146,7 @@ ng serve
Your Angular app will compile and serve locally, visit it we should see an empty list.
-In another tab [start adding data to an `items` collection in Firestore](https://firebase.corp.google.com/project/_/database/firestore/data). *As we're not authenticating users yet, be sure to start Firestore in **test mode** or allow reading from the `items` collection in Security Rules (`allow read: if true`).*
+In another tab [start adding data to an `items` collection in Firestore](https://firebase.google.com/docs/firestore/manage-data/add-data). *As we're not authenticating users yet, be sure to start Firestore in **test mode** or allow reading from the `items` collection in Security Rules (`allow read: if true`).*
Once you've created a `items` collection and are inserting documents, you should see data streaming into your Angular application.
diff --git a/docs/performance/getting-started.md b/docs/performance/getting-started.md
index 15cf105ce..5b1832367 100644
--- a/docs/performance/getting-started.md
+++ b/docs/performance/getting-started.md
@@ -52,12 +52,12 @@ You can inject `AngularFirePerformance` to perform manual traces.
```ts
constructor(private performance: AngularFirePerformance) {}
-ngOnInit() {
- const trace = this.performance.trace('some-trace');
- trace.start();
- ...
- trace.stop();
-}
+...
+
+const trace = await this.performance.trace('some-trace');
+trace.start();
+...
+trace.stop();
```
## RXJS operators
diff --git a/package.json b/package.json
index 0cb99e87a..f87e48af1 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@angular/fire",
- "version": "6.0.0",
+ "version": "6.0.1",
"description": "The official library of Firebase and Angular.",
"private": true,
"scripts": {
@@ -16,6 +16,11 @@
"build": "tsc tools/build.ts; node ./tools/build.js",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 1"
},
+ "husky": {
+ "hooks": {
+ "pre-commit": "npm run lint"
+ }
+ },
"schematics": "./dist/packages-dist/collection.json",
"builders": "./dist/packages-dist/builders.json",
"keywords": [
@@ -34,23 +39,25 @@
},
"homepage": "https://github.com/angular/angularfire2#readme",
"dependencies": {
- "@angular-devkit/architect": "~0.900",
- "@angular-devkit/core": "^9.0.0",
- "@angular-devkit/schematics": "^9.0.0",
- "@angular/common": "^9.0.0",
- "@angular/compiler": "^9.0.0",
- "@angular/core": "^9.0.0",
- "@angular/platform-browser": "^9.0.0",
- "@angular/platform-browser-dynamic": "^9.0.0",
- "@angular/router": "^9.0.0",
+ "@angular-devkit/architect": ">= 0.900 < 0.1100",
+ "@angular-devkit/core": "^9.0.0 || ^10.0.0",
+ "@angular-devkit/schematics": "^9.0.0 || ^10.0.0",
+ "@angular/common": "^9.0.0 || ^10.0.0",
+ "@angular/compiler": "^9.0.0 || ^10.0.0",
+ "@angular/core": "^9.0.0 || ^10.0.0",
+ "@angular/platform-browser": "^9.0.0 || ^10.0.0",
+ "@angular/platform-browser-dynamic": "^9.0.0 || ^10.0.0",
+ "@angular/router": "^9.0.0 || ^10.0.0",
"firebase": "^7.13.1",
"firebase-admin": "^8.10.0",
"firebase-functions": "^3.6.0",
"firebase-tools": "^8.0.0",
"fs-extra": "^8.0.1",
"fuzzy": "^0.1.3",
+ "husky": "^4.2.5",
"inquirer": "^6.2.2",
"inquirer-autocomplete-prompt": "^1.0.1",
+ "open": "^7.0.3",
"rxfire": "^3.9.7",
"rxjs": "^6.5.3",
"semver": "^7.1.3",
@@ -64,12 +71,12 @@
"utf-8-validate": "^5.0.2"
},
"devDependencies": {
- "@angular-devkit/build-angular": "~0.900",
- "@angular-devkit/build-ng-packagr": "~0.900",
- "@angular/animations": " ^9.0.0",
- "@angular/cli": "^9.0.0",
- "@angular/compiler-cli": "^9.0.0",
- "@angular/platform-server": "^9.0.0",
+ "@angular-devkit/build-angular": ">= 0.900 < 0.1100",
+ "@angular-devkit/build-ng-packagr": ">= 0.900 < 0.1100",
+ "@angular/animations": " ^9.0.0 || ^10.0.0",
+ "@angular/cli": "^9.0.0 || ^10.0.0",
+ "@angular/compiler-cli": "^9.0.0 || ^10.0.0",
+ "@angular/platform-server": "^9.0.0 || ^10.0.0",
"@types/fs-extra": "^7.0.0",
"@types/gzip-size": "^5.1.1",
"@types/inquirer": "^0.0.44",
@@ -80,7 +87,7 @@
"codelyzer": "^5.0.0",
"concurrently": "^2.2.0",
"conventional-changelog-cli": "^1.2.0",
- "firebase-functions-test": "^0.1.7",
+ "firebase-functions-test": "^0.2.0",
"gzip-size": "^5.1.1",
"jasmine": "^3.4.0",
"jasmine-core": "^3.4.0",
diff --git a/sample/angular.json b/sample/angular.json
index 9b326a8ab..5ac57205f 100644
--- a/sample/angular.json
+++ b/sample/angular.json
@@ -126,7 +126,6 @@
"tsConfig": "tsconfig.server.json",
"bundleDependencies": false,
"externalDependencies": [
- "@firebase/firestore",
"@firebase/firestore"
]
},
diff --git a/sample/firebase.json b/sample/firebase.json
index 1857f85f6..f92bcbba1 100644
--- a/sample/firebase.json
+++ b/sample/firebase.json
@@ -4,10 +4,15 @@
"target": "sample",
"public": "dist/sample/dist/sample/browser",
"ignore": [
- "firebase.json",
- "**/.*",
- "**/node_modules/**"
+ "**/.*"
],
+ "headers": [{
+ "source": "*.[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f].+(css|js)",
+ "headers": [{
+ "key": "Cache-Control",
+ "value": "public,max-age=31536000,immutable"
+ }]
+ }],
"rewrites": [
{
"source": "**",
diff --git a/sample/package.json b/sample/package.json
index ca6f187e3..ba87dadb0 100644
--- a/sample/package.json
+++ b/sample/package.json
@@ -19,7 +19,7 @@
"@angular/common": "~9.1.0",
"@angular/compiler": "~9.1.0",
"@angular/core": "~9.1.0",
- "@angular/fire": "file:../dist/packages-dist",
+ "@angular/fire": "../dist/packages-dist",
"@angular/forms": "~9.1.0",
"@angular/platform-browser": "~9.1.0",
"@angular/platform-browser-dynamic": "~9.1.0",
@@ -27,7 +27,8 @@
"@angular/router": "~9.1.0",
"@angular/service-worker": "~9.1.0",
"@nguniversal/express-engine": "~9.1.0",
- "firebase": "^7.13.1",
+ "firebase": "^7.13.2",
+ "proxy-polyfill": "^0.3.1",
"rxjs": "~6.5.4",
"tslib": "^1.10.0",
"zone.js": "~0.10.2"
diff --git a/sample/server.ts b/sample/server.ts
index 329e5797a..c59772b10 100644
--- a/sample/server.ts
+++ b/sample/server.ts
@@ -9,8 +9,10 @@ import { APP_BASE_HREF } from '@angular/common';
import { existsSync } from 'fs';
// Polyfill XMLHttpRequest and WS for Firebase
-global['XMLHttpRequest'] = require("xhr2");
-global['WebSocket'] = require("ws");
+/* tslint:disable:no-string-literal */
+global['XMLHttpRequest'] = require('xhr2');
+global['WebSocket'] = require('ws');
+/* tslint:enable:no-string-literal */
// The Express app is exported so that it can be used by serverless Functions.
export function app() {
diff --git a/sample/src/app/app-routing.module.ts b/sample/src/app/app-routing.module.ts
index db7155ca0..61b02b7c2 100644
--- a/sample/src/app/app-routing.module.ts
+++ b/sample/src/app/app-routing.module.ts
@@ -1,13 +1,16 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
+import { HomeComponent } from './home/home.component';
-const routes: Routes = [];
+const routes: Routes = [
+ { path: '', component: HomeComponent, pathMatch: 'full' }
+];
@NgModule({
imports: [RouterModule.forRoot(routes, {
initialNavigation: 'enabled'
-})],
+ })],
exports: [RouterModule]
})
export class AppRoutingModule { }
diff --git a/sample/src/app/app.component.spec.ts b/sample/src/app/app.component.spec.ts
index 58e834776..480adc6b2 100644
--- a/sample/src/app/app.component.spec.ts
+++ b/sample/src/app/app.component.spec.ts
@@ -1,4 +1,4 @@
-import { TestBed, async } from '@angular/core/testing';
+import { async, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
diff --git a/sample/src/app/app.component.ts b/sample/src/app/app.component.ts
index 280a572ca..e0cb7c544 100644
--- a/sample/src/app/app.component.ts
+++ b/sample/src/app/app.component.ts
@@ -1,20 +1,14 @@
-import { Component, ApplicationRef } from '@angular/core';
+import { ApplicationRef, Component } from '@angular/core';
import { FirebaseApp } from '@angular/fire';
@Component({
selector: 'app-root',
template: `
- Hello world!
- {{ firebaseApp.name }}
-
-
-
-
+
`,
styles: [``]
})
export class AppComponent {
- title = 'sample';
constructor(public readonly firebaseApp: FirebaseApp, appRef: ApplicationRef) {
appRef.isStable.subscribe(it => console.log('isStable', it));
}
diff --git a/sample/src/app/app.module.ts b/sample/src/app/app.module.ts
index d6fae8dbf..c24b599c4 100644
--- a/sample/src/app/app.module.ts
+++ b/sample/src/app/app.module.ts
@@ -1,5 +1,5 @@
import { BrowserModule, BrowserTransferStateModule } from '@angular/platform-browser';
-import { NgModule, isDevMode } from '@angular/core';
+import { isDevMode, NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@@ -9,7 +9,12 @@ import { environment } from '../environments/environment';
import { AngularFireModule } from '@angular/fire';
import { AngularFireStorageModule } from '@angular/fire/storage';
-import { AngularFireAnalyticsModule, UserTrackingService, ScreenTrackingService, DEBUG_MODE as ANALYTICS_DEBUG_MODE } from '@angular/fire/analytics';
+import {
+ AngularFireAnalyticsModule,
+ DEBUG_MODE as ANALYTICS_DEBUG_MODE,
+ ScreenTrackingService,
+ UserTrackingService
+} from '@angular/fire/analytics';
import { FirestoreComponent } from './firestore/firestore.component';
@@ -24,11 +29,21 @@ import { AngularFireAuthGuardModule } from '@angular/fire/auth-guard';
import { DatabaseComponent } from './database/database.component';
import { StorageComponent } from './storage/storage.component';
import { RemoteConfigComponent } from './remote-config/remote-config.component';
+import { HomeComponent } from './home/home.component';
+import { AuthComponent } from './auth/auth.component';
const shouldUseEmulator = () => false;
@NgModule({
- declarations: [ AppComponent, StorageComponent, FirestoreComponent, DatabaseComponent, RemoteConfigComponent ],
+ declarations: [
+ AppComponent,
+ StorageComponent,
+ FirestoreComponent,
+ DatabaseComponent,
+ RemoteConfigComponent,
+ HomeComponent,
+ AuthComponent
+ ],
imports: [
BrowserModule.withServerTransition({ appId: 'serverApp' }),
BrowserTransferStateModule,
@@ -50,12 +65,19 @@ const shouldUseEmulator = () => false;
UserTrackingService,
ScreenTrackingService,
PerformanceMonitoringService,
- { provide: ANALYTICS_DEBUG_MODE, useFactory: () => isDevMode() },
- { provide: DATABASE_URL, useFactory: () => shouldUseEmulator() ? `http://localhost:9000?ns=${environment.firebase.projectId}` : undefined },
- { provide: FIRESTORE_SETTINGS, useFactory: () => shouldUseEmulator() ? { host: 'localhost:8080', ssl: false } : {} },
- { provide: FUNCTIONS_ORIGIN, useFactory: () => shouldUseEmulator() ? 'http://localhost:9999' : undefined },
+ {
+ provide: ANALYTICS_DEBUG_MODE,
+ useFactory: () => isDevMode()
+ },
+ {
+ provide: DATABASE_URL,
+ useFactory: () => shouldUseEmulator() ? `http://localhost:9000?ns=${environment.firebase.projectId}` : undefined
+ },
+ { provide: FIRESTORE_SETTINGS, useFactory: () => shouldUseEmulator() ? { host: 'localhost:8080', ssl: false } : {} },
+ { provide: FUNCTIONS_ORIGIN, useFactory: () => shouldUseEmulator() ? 'http://localhost:9999' : undefined },
{ provide: REMOTE_CONFIG_SETTINGS, useFactory: () => isDevMode() ? { minimumFetchIntervalMillis: 10_000 } : {} }
],
- bootstrap: [ AppComponent ]
+ bootstrap: [AppComponent]
})
-export class AppModule { }
+export class AppModule {
+}
diff --git a/sample/src/app/auth/auth.component.ts b/sample/src/app/auth/auth.component.ts
index 7a0cf3c7c..28cf780a3 100644
--- a/sample/src/app/auth/auth.component.ts
+++ b/sample/src/app/auth/auth.component.ts
@@ -1,19 +1,51 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, OnDestroy } from '@angular/core';
+import { AngularFireAuth } from '@angular/fire/auth';
+import { auth as rawAuth } from 'firebase/app';
+import { Subscription } from 'rxjs';
+import { map } from 'rxjs/operators';
+import { trace } from '@angular/fire/performance';
@Component({
selector: 'app-auth',
template: `
- auth works!
+ Auth!
+ {{ (auth.user | async)?.uid | json }}
+ Log in with Google
+ Log out
`,
styles: []
})
-export class AuthComponent implements OnInit {
+export class AuthComponent implements OnInit, OnDestroy {
- constructor() { }
+ private readonly userDisposable: Subscription;
- ngOnInit(): void {
+ showLoginButton = false;
+ showLogoutButton = false;
+
+ constructor(public readonly auth: AngularFireAuth) {
+ this.userDisposable = this.auth.authState.pipe(
+ trace('auth'),
+ map(u => !!u)
+ ).subscribe(isLoggedIn => {
+ this.showLoginButton = !isLoggedIn;
+ this.showLogoutButton = isLoggedIn;
+ });
+ }
+
+ ngOnInit(): void { }
+
+ ngOnDestroy(): void {
+ this.userDisposable.unsubscribe();
+ }
+
+ login() {
+ this.auth.signInWithPopup(new rawAuth.GoogleAuthProvider());
+ }
+
+ logout() {
+ this.auth.signOut();
}
}
diff --git a/sample/src/app/database/database.component.ts b/sample/src/app/database/database.component.ts
index b5136a874..5367f9d4e 100644
--- a/sample/src/app/database/database.component.ts
+++ b/sample/src/app/database/database.component.ts
@@ -1,7 +1,7 @@
import { Component, OnInit } from '@angular/core';
import { AngularFireDatabase } from '@angular/fire/database';
import { Observable } from 'rxjs';
-import { TransferState, makeStateKey } from '@angular/platform-browser';
+import { makeStateKey, TransferState } from '@angular/platform-browser';
import { startWith, tap } from 'rxjs/operators';
import { trace } from '@angular/fire/performance';
diff --git a/sample/src/app/firestore/firestore.component.ts b/sample/src/app/firestore/firestore.component.ts
index 09be15bea..d01e0cf1c 100644
--- a/sample/src/app/firestore/firestore.component.ts
+++ b/sample/src/app/firestore/firestore.component.ts
@@ -1,8 +1,8 @@
import { Component, OnInit } from '@angular/core';
import { AngularFirestore } from '@angular/fire/firestore';
import { Observable } from 'rxjs';
-import { tap, startWith } from 'rxjs/operators';
-import { TransferState, makeStateKey } from '@angular/platform-browser';
+import { startWith, tap } from 'rxjs/operators';
+import { makeStateKey, TransferState } from '@angular/platform-browser';
import { trace } from '@angular/fire/performance';
@Component({
diff --git a/sample/src/app/home/home.component.ts b/sample/src/app/home/home.component.ts
new file mode 100644
index 000000000..7fdd06e07
--- /dev/null
+++ b/sample/src/app/home/home.component.ts
@@ -0,0 +1,20 @@
+import { Component } from '@angular/core';
+import { FirebaseApp } from '@angular/fire';
+
+@Component({
+ selector: 'app-home',
+ template: `
+ Hello world!
+ {{ firebaseApp.name }}
+
+
+
+
+
+ `,
+ styles: [``]
+})
+export class HomeComponent {
+ constructor(public readonly firebaseApp: FirebaseApp) {
+ }
+}
diff --git a/sample/src/app/remote-config/remote-config.component.ts b/sample/src/app/remote-config/remote-config.component.ts
index 9bd3af5e5..a2d3311c5 100644
--- a/sample/src/app/remote-config/remote-config.component.ts
+++ b/sample/src/app/remote-config/remote-config.component.ts
@@ -1,5 +1,5 @@
import { Component, OnInit } from '@angular/core';
-import { AngularFireRemoteConfig } from '@angular/fire/remote-config';
+import { AngularFireRemoteConfig, mapToObject } from '@angular/fire/remote-config';
import { trace } from '@angular/fire/performance';
import { Observable } from 'rxjs';
@@ -18,7 +18,7 @@ export class RemoteConfigComponent implements OnInit {
readonly change$: Observable;
constructor(public readonly remoteConfig: AngularFireRemoteConfig) {
- this.change$ = remoteConfig.changes.pipe(trace('remote-config'));
+ this.change$ = remoteConfig.parameters.pipe(trace('remote-config'), mapToObject({}));
}
ngOnInit(): void {
diff --git a/sample/src/app/storage/storage.component.ts b/sample/src/app/storage/storage.component.ts
index 077741ab5..b8e3547ee 100644
--- a/sample/src/app/storage/storage.component.ts
+++ b/sample/src/app/storage/storage.component.ts
@@ -5,14 +5,15 @@ import { startWith, tap } from 'rxjs/operators';
import { makeStateKey, TransferState } from '@angular/platform-browser';
import { trace } from '@angular/fire/performance';
-const TRANSPARENT_PNG = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='
+const TRANSPARENT_PNG
+ = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';
@Component({
selector: 'app-storage',
template: `
Storage!
-
+
`,
styles: []
diff --git a/sample/src/environments/environment.prod.ts b/sample/src/environments/environment.prod.ts
index c4610812a..77e62a543 100644
--- a/sample/src/environments/environment.prod.ts
+++ b/sample/src/environments/environment.prod.ts
@@ -1,13 +1,13 @@
export const environment = {
production: true,
firebase: {
- apiKey: "AIzaSyA7CNE9aHbcSEbt9y03QReJ-Xr0nwKg7Yg",
- authDomain: "aftest-94085.firebaseapp.com",
- databaseURL: "https://aftest-94085.firebaseio.com",
- projectId: "aftest-94085",
- storageBucket: "aftest-94085.appspot.com",
- messagingSenderId: "480362569154",
- appId: "1:480362569154:web:2fe6f75104cdfb82f50a5b",
- measurementId: "G-CBRYER9PJR"
+ apiKey: 'AIzaSyA7CNE9aHbcSEbt9y03QReJ-Xr0nwKg7Yg',
+ authDomain: 'aftest-94085.firebaseapp.com',
+ databaseURL: 'https://aftest-94085.firebaseio.com',
+ projectId: 'aftest-94085',
+ storageBucket: 'aftest-94085.appspot.com',
+ messagingSenderId: '480362569154',
+ appId: '1:480362569154:web:2fe6f75104cdfb82f50a5b',
+ measurementId: 'G-CBRYER9PJR'
}
};
diff --git a/sample/src/environments/environment.ts b/sample/src/environments/environment.ts
index 0acc17a8d..6e0d7c872 100644
--- a/sample/src/environments/environment.ts
+++ b/sample/src/environments/environment.ts
@@ -5,14 +5,14 @@
export const environment = {
production: false,
firebase: {
- apiKey: "AIzaSyA7CNE9aHbcSEbt9y03QReJ-Xr0nwKg7Yg",
- authDomain: "aftest-94085.firebaseapp.com",
- databaseURL: "https://aftest-94085.firebaseio.com",
- projectId: "aftest-94085",
- storageBucket: "aftest-94085.appspot.com",
- messagingSenderId: "480362569154",
- appId: "1:480362569154:web:2fe6f75104cdfb82f50a5b",
- measurementId: "G-CBRYER9PJR"
+ apiKey: 'AIzaSyA7CNE9aHbcSEbt9y03QReJ-Xr0nwKg7Yg',
+ authDomain: 'aftest-94085.firebaseapp.com',
+ databaseURL: 'https://aftest-94085.firebaseio.com',
+ projectId: 'aftest-94085',
+ storageBucket: 'aftest-94085.appspot.com',
+ messagingSenderId: '480362569154',
+ appId: '1:480362569154:web:2fe6f75104cdfb82f50a5b',
+ measurementId: 'G-CBRYER9PJR'
}
};
@@ -23,4 +23,4 @@ export const environment = {
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
-import 'zone.js/dist/zone-error'; // Included with Angular CLI.
+import 'zone.js/dist/zone-error'; // Included with Angular CLI.
diff --git a/sample/src/polyfills.ts b/sample/src/polyfills.ts
index 03711e5d9..ad4c19af5 100644
--- a/sample/src/polyfills.ts
+++ b/sample/src/polyfills.ts
@@ -55,9 +55,11 @@
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
-import 'zone.js/dist/zone'; // Included with Angular CLI.
+import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
+
+import 'proxy-polyfill/proxy.min.js';
diff --git a/sample/src/test.ts b/sample/src/test.ts
index 50193eb0f..ea29c06f0 100644
--- a/sample/src/test.ts
+++ b/sample/src/test.ts
@@ -2,10 +2,7 @@
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
-import {
- BrowserDynamicTestingModule,
- platformBrowserDynamicTesting
-} from '@angular/platform-browser-dynamic/testing';
+import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
declare const require: {
context(path: string, deep?: boolean, filter?: RegExp): {
diff --git a/sample/yarn.lock b/sample/yarn.lock
index 843ce9c7d..84ea3c5c9 100644
--- a/sample/yarn.lock
+++ b/sample/yarn.lock
@@ -181,8 +181,8 @@
resolved "https://registry.yarnpkg.com/@angular/core/-/core-9.1.0.tgz#9dfc386bd1461e0fd4786031fd245da04371421c"
integrity sha512-RVlyegdIAij0P1wLY5ObIdsBAzvmHkHfElnmfiNKhaDftP6U/3zRtaKDu0bq0jvn1WCQ8zXxFQ8AWyKZwyFS+w==
-"@angular/fire@file:../dist/packages-dist":
- version "6.0.0"
+"@angular/fire@../dist/packages-dist":
+ version "6.0.1"
"@angular/forms@~9.1.0":
version "9.1.0"
@@ -977,16 +977,16 @@
resolved "https://registry.yarnpkg.com/@firebase/analytics-types/-/analytics-types-0.3.0.tgz#33c3f695313b561d48d18d663a20f20362d3ee7c"
integrity sha512-0AJ6xn53Qn0D/YOVHHvlWFfnzzRSdd98Lr8Oqe1PJ2HPIN+o7qf03YmOG7fLpR1uplcWd+7vGKmxUrN3jKUBwg==
-"@firebase/analytics@0.3.0":
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/@firebase/analytics/-/analytics-0.3.0.tgz#943e73e792ba3f282df4d47aff5a1603b93b37a5"
- integrity sha512-EEHuK+OcWH6UxufRLVU3lAJ4rmm7aVHmcgkhE9ZQJQy5c+w7QTLvVpGqtrpqD+cYyIBJkFBKJB8NziVyqKwQHw==
+"@firebase/analytics@0.3.1":
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/@firebase/analytics/-/analytics-0.3.1.tgz#2c81837b1ecfc8405ee9ba8e9051c0753608e441"
+ integrity sha512-/Fo4tU+tVADVirH/RPOorirlQWxDw0oR0TJJoZH9F7+B/IZi8DHdZSl2gOVYBOa9jrL2jA7lNV8VGkWXLXhcXg==
dependencies:
"@firebase/analytics-types" "0.3.0"
- "@firebase/component" "0.1.8"
- "@firebase/installations" "0.4.6"
- "@firebase/logger" "0.2.0"
- "@firebase/util" "0.2.43"
+ "@firebase/component" "0.1.9"
+ "@firebase/installations" "0.4.7"
+ "@firebase/logger" "0.2.1"
+ "@firebase/util" "0.2.44"
tslib "1.11.1"
"@firebase/app-types@0.6.0":
@@ -994,15 +994,15 @@
resolved "https://registry.yarnpkg.com/@firebase/app-types/-/app-types-0.6.0.tgz#8dcc3e793c6983e9d54f7eb623a7618c05f2d94c"
integrity sha512-ld6rzjXk/SUauHiQZJkeuSJpxIZ5wdnWuF5fWBFQNPaxsaJ9kyYg9GqEvwZ1z2e6JP5cU9gwRBlfW1WkGtGDYA==
-"@firebase/app@0.6.0":
- version "0.6.0"
- resolved "https://registry.yarnpkg.com/@firebase/app/-/app-0.6.0.tgz#62f1720a8764333d32a11b14f1c46d82f536cf4e"
- integrity sha512-utFL07aO64ZVs9g79cv1KHomtLdKkkAeKN5e8G9NlXXuO6dZXhcHLbOmKY1AfwrkAvUzPEKkFFY3dytOIt+nlg==
+"@firebase/app@0.6.1":
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/@firebase/app/-/app-0.6.1.tgz#ce7d2bb530e1fd0cb2fb74c85509249c783647fe"
+ integrity sha512-KSzSFQfiJgxi+7Ff/EZQcdvCnqKj2db9xa7I8z1UoRIRez9e0Q6+GpW3mrSVmmSCrBbKYsOO/SJh5NaFot0evg==
dependencies:
"@firebase/app-types" "0.6.0"
- "@firebase/component" "0.1.8"
- "@firebase/logger" "0.2.0"
- "@firebase/util" "0.2.43"
+ "@firebase/component" "0.1.9"
+ "@firebase/logger" "0.2.1"
+ "@firebase/util" "0.2.44"
dom-storage "2.1.0"
tslib "1.11.1"
xmlhttprequest "1.8.0"
@@ -1017,10 +1017,10 @@
resolved "https://registry.yarnpkg.com/@firebase/auth-types/-/auth-types-0.10.0.tgz#9403633e723336055fad4bbf5e4c9fe3c55f8d3f"
integrity sha512-VuW7c+RAk3AYPU0Hxmun3RzXn7fbJDdjQbxvvpRMnQ9zrhk8mH42cY466M0n4e/UGQ+0smlx5BqZII8aYQ5XPg==
-"@firebase/auth@0.14.1":
- version "0.14.1"
- resolved "https://registry.yarnpkg.com/@firebase/auth/-/auth-0.14.1.tgz#0cb3226025c27bf2e01a738f3841ab6bde80572e"
- integrity sha512-LE+QED10cjp28jJ7wwIY58HQBXoJioEWiQy7iQS8Fo2UxHGY5BvGjwnxX4yyszQPbcZZRLDSlBIUaYfog+rdEA==
+"@firebase/auth@0.14.2":
+ version "0.14.2"
+ resolved "https://registry.yarnpkg.com/@firebase/auth/-/auth-0.14.2.tgz#2f5356cd7893ebd26ea6c4a0933eae6f6e0a0aff"
+ integrity sha512-5HaEGne2JbcVvzK9FeOEGi8NNQwq00thmL88uduqI8LTXHSOWaC8Y4El9DesVu6aFEOXELpf7W4I34CG9WwjlA==
dependencies:
"@firebase/auth-types" "0.10.0"
@@ -1032,6 +1032,14 @@
"@firebase/util" "0.2.43"
tslib "1.11.1"
+"@firebase/component@0.1.9":
+ version "0.1.9"
+ resolved "https://registry.yarnpkg.com/@firebase/component/-/component-0.1.9.tgz#738d1a4c79431a1e899df5de7b310eee370d34ed"
+ integrity sha512-i58GsVpxBGnKn1rx2RCAH0rk1Ldp6WterfBNDHyxmuyRO6BaZAgvxrZ3Ku1/lqiI7XMbmmRpP3emmwrStbFt9Q==
+ dependencies:
+ "@firebase/util" "0.2.44"
+ tslib "1.11.1"
+
"@firebase/database-types@0.4.14":
version "0.4.14"
resolved "https://registry.yarnpkg.com/@firebase/database-types/-/database-types-0.4.14.tgz#181e10c1d1ae64fd0a080f6e0369cec115c51d70"
@@ -1039,7 +1047,20 @@
dependencies:
"@firebase/app-types" "0.6.0"
-"@firebase/database@0.5.24", "@firebase/database@^0.5.17":
+"@firebase/database@0.5.25":
+ version "0.5.25"
+ resolved "https://registry.yarnpkg.com/@firebase/database/-/database-0.5.25.tgz#fa3b00cf315cebd7a29ca1dfd869853b61469534"
+ integrity sha512-qUIpgDoODWs/FEdCQoH/VwRDvW7nn7m99TGxbMhdiE2WV/nzKbCo/PbbGm0dltdZzQ/SE87E2lfpPGK89Riw6Q==
+ dependencies:
+ "@firebase/auth-interop-types" "0.1.4"
+ "@firebase/component" "0.1.9"
+ "@firebase/database-types" "0.4.14"
+ "@firebase/logger" "0.2.1"
+ "@firebase/util" "0.2.44"
+ faye-websocket "0.11.3"
+ tslib "1.11.1"
+
+"@firebase/database@^0.5.17":
version "0.5.24"
resolved "https://registry.yarnpkg.com/@firebase/database/-/database-0.5.24.tgz#548b2030def35a7b6f4d71a4b2d1a67499d1eef3"
integrity sha512-9whAQzU8cxDUKGBWCT/aHVmqfyzCP2RkGhbZi2oHpMrmvht7cuBtXtUbDD5R8WomniCOUP8rtQfmCFI7V9ehYw==
@@ -1057,15 +1078,15 @@
resolved "https://registry.yarnpkg.com/@firebase/firestore-types/-/firestore-types-1.10.1.tgz#bf018f9c495f470592de745389474dc1c2960d3f"
integrity sha512-vyKdm+AYUFT8XeUX62IOqaqPFCs/mAMoSEsqIz9HnSVsqCw/IocNjtjSa+3M80kRw4V8fI7JI+Xz6Wg5VJXLqA==
-"@firebase/firestore@1.13.0":
- version "1.13.0"
- resolved "https://registry.yarnpkg.com/@firebase/firestore/-/firestore-1.13.0.tgz#2638f25b2a3d19cb891a6b334a0dc14aefef5a34"
- integrity sha512-GctO+sxLqOnY8SkBN5Z5p1nUYRX+yWSc9Kcx9nIPbUZ0WqBM5BaSlBHZHTFtjmJxS+0j/Y8Mu7c6qm6q5rVnjA==
+"@firebase/firestore@1.13.1":
+ version "1.13.1"
+ resolved "https://registry.yarnpkg.com/@firebase/firestore/-/firestore-1.13.1.tgz#272485845ec9796c52237b57d81ce9943553b95e"
+ integrity sha512-VcJCaj8Sxt89tLiL5q7szkdQ141KCsNanTAGBNdDWtHU4TbL66DKNgk798jopzN4LIhK2nnX34ntNek2p6r9PQ==
dependencies:
- "@firebase/component" "0.1.8"
+ "@firebase/component" "0.1.9"
"@firebase/firestore-types" "1.10.1"
- "@firebase/logger" "0.2.0"
- "@firebase/util" "0.2.43"
+ "@firebase/logger" "0.2.1"
+ "@firebase/util" "0.2.44"
"@firebase/webchannel-wrapper" "0.2.38"
"@grpc/proto-loader" "^0.5.0"
grpc "1.24.2"
@@ -1076,12 +1097,12 @@
resolved "https://registry.yarnpkg.com/@firebase/functions-types/-/functions-types-0.3.16.tgz#be0362d7f61648fdf36a7d95de239eddee88f931"
integrity sha512-kHhBvSYiY2prY4vNQCALYs1+OruTdylvGemHG6G6Bs/rj3qw7ui3WysBsDU/rInJitHIcsZ35qrtanoJeQUIXQ==
-"@firebase/functions@0.4.38":
- version "0.4.38"
- resolved "https://registry.yarnpkg.com/@firebase/functions/-/functions-0.4.38.tgz#f49e3f4a4a3c67e527c472f05b2e517ea057e7fc"
- integrity sha512-t5QkJg251FmIEEi2mh3Xrf7Q3yonSLRaUW/vhgze7A3Xy3uTIUT3BVNWuKaRmg1n0PgKQaBHCjlDoLJAdSpujg==
+"@firebase/functions@0.4.39":
+ version "0.4.39"
+ resolved "https://registry.yarnpkg.com/@firebase/functions/-/functions-0.4.39.tgz#1cb8289caf43d5bae30a25734760a03fcdfe5714"
+ integrity sha512-D44hDaUFXsu6a+riagFzKP6NiAW2H//gLh9NwRLaEvwSopPF6irw1OmAA1fylFhugRgdiKJr0n13BxEN1/l9lQ==
dependencies:
- "@firebase/component" "0.1.8"
+ "@firebase/component" "0.1.9"
"@firebase/functions-types" "0.3.16"
"@firebase/messaging-types" "0.4.4"
isomorphic-fetch "2.2.1"
@@ -1092,14 +1113,14 @@
resolved "https://registry.yarnpkg.com/@firebase/installations-types/-/installations-types-0.3.3.tgz#f2e49e73afaeb7b352250365d0d90dff0b792592"
integrity sha512-XvWhPPAGeZlc+CfCA8jTt2pv19Jovi/nUV73u30QbjBbk5xci9bp5I29aBZukHsR6YNBjFCLSkLPbno4m/bLUg==
-"@firebase/installations@0.4.6":
- version "0.4.6"
- resolved "https://registry.yarnpkg.com/@firebase/installations/-/installations-0.4.6.tgz#e288e18b39dda3e8b3b35c3be6185a7b187924e5"
- integrity sha512-ey8cE2ldRO4pYqg0lCWQ+XFLETHZWha3Hw1CnYJjLivk4FMM8u/es3Oa257wwtYXAUfr0UsPDfHFgYME9E9EhA==
+"@firebase/installations@0.4.7":
+ version "0.4.7"
+ resolved "https://registry.yarnpkg.com/@firebase/installations/-/installations-0.4.7.tgz#143d1bfb20fbcd00e826e7afe283527c329e3c25"
+ integrity sha512-Fz9B/H58xfkAnDGVTW1V/73Jd5LAN9o5dpz1K3+u2W+zp0iU6HoRTwm9Bgk+aV6/0FYjqmtYEDjK8T0OYPoIQA==
dependencies:
- "@firebase/component" "0.1.8"
+ "@firebase/component" "0.1.9"
"@firebase/installations-types" "0.3.3"
- "@firebase/util" "0.2.43"
+ "@firebase/util" "0.2.44"
idb "3.0.2"
tslib "1.11.1"
@@ -1108,20 +1129,25 @@
resolved "https://registry.yarnpkg.com/@firebase/logger/-/logger-0.2.0.tgz#d40149b8a33bca3dfbfb5b4a63e06b3ffa193157"
integrity sha512-qOMnAh1JY9NkYUEy3iFviiFq0dCvk6qN2DsRy2Y7eAhHR6RqwA47l1kI+0MIXmSzlJ9akXjWAXxV5ijzr68Big==
+"@firebase/logger@0.2.1":
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/@firebase/logger/-/logger-0.2.1.tgz#09cdc5d3fe8ba4ed9bf8d6e6ab2a3b5398bd80bb"
+ integrity sha512-H4nttTqUzEw3TA/JYl8ma6oMSNKHcdpEWV2L2qA+ZEcpM2OLAzagi//DrYBFR5xpPb17IGagpzSxFgx937Sq/A==
+
"@firebase/messaging-types@0.4.4":
version "0.4.4"
resolved "https://registry.yarnpkg.com/@firebase/messaging-types/-/messaging-types-0.4.4.tgz#bef66157bdd3ddaafd6d48f1c5ee973fdc385f84"
integrity sha512-JGtkr+1A1Dw7+yCqQigqBfGKtq0gTCruFScBD4MVjqZHiqGIYpnQisWnpGbkzPR6aOt6iQxgwxUhHG1ulUQGeg==
-"@firebase/messaging@0.6.10":
- version "0.6.10"
- resolved "https://registry.yarnpkg.com/@firebase/messaging/-/messaging-0.6.10.tgz#0f2713a85732ce23ec7968e2f0ac784a259330e4"
- integrity sha512-WYnKEffG6m0EMHzib8KCWVUGno1cuBC13RrOfGWOCv/whdy9QCIZgMxH/NsY3BrYst8FnjuXEU16fi5AEf4qbg==
+"@firebase/messaging@0.6.11":
+ version "0.6.11"
+ resolved "https://registry.yarnpkg.com/@firebase/messaging/-/messaging-0.6.11.tgz#8be61ba73106c3907a0526758a038da6c09fa286"
+ integrity sha512-rYLNX9XjH3nmJ20KeMta78npGah65nvv+4zOIW8DJVs9jXJO6YSah4q0ugQESVC6H9VUFZw1e6iM9e75WHhxFA==
dependencies:
- "@firebase/component" "0.1.8"
- "@firebase/installations" "0.4.6"
+ "@firebase/component" "0.1.9"
+ "@firebase/installations" "0.4.7"
"@firebase/messaging-types" "0.4.4"
- "@firebase/util" "0.2.43"
+ "@firebase/util" "0.2.44"
idb "3.0.2"
tslib "1.11.1"
@@ -1130,16 +1156,16 @@
resolved "https://registry.yarnpkg.com/@firebase/performance-types/-/performance-types-0.0.12.tgz#15fa79e296b502e21054a66c9e7ded59398fd8a7"
integrity sha512-eIDF7CHetOE5sc+hCaUebEn/2Aiaju7UkgZDTl7lNQHz5fK9wJ/11HaE8WdnDr//ngS3lQAGC2RB4lAZeEWraA==
-"@firebase/performance@0.2.36":
- version "0.2.36"
- resolved "https://registry.yarnpkg.com/@firebase/performance/-/performance-0.2.36.tgz#341bad35e786a377a38c4b4165088ebd4f07d025"
- integrity sha512-nMx3gT+ZD86MV5n460XFA1o75YYMGcs2MXrJa462rfUQtqOrtOOvdUvVtmE6cLrHsL4Y83B+VBWKHzACIPghPw==
+"@firebase/performance@0.2.37":
+ version "0.2.37"
+ resolved "https://registry.yarnpkg.com/@firebase/performance/-/performance-0.2.37.tgz#d77be238553ed12465ef0ccdf5275474b8202321"
+ integrity sha512-BL4/19Hh5nyq6aaoo2b87rDHf2hSX/UOl0GBw7hDkWXYRodARtbQvIfhb+CZ8IlZn0Xg0x8SyNIpSCIp9UfYlA==
dependencies:
- "@firebase/component" "0.1.8"
- "@firebase/installations" "0.4.6"
- "@firebase/logger" "0.2.0"
+ "@firebase/component" "0.1.9"
+ "@firebase/installations" "0.4.7"
+ "@firebase/logger" "0.2.1"
"@firebase/performance-types" "0.0.12"
- "@firebase/util" "0.2.43"
+ "@firebase/util" "0.2.44"
tslib "1.11.1"
"@firebase/polyfill@0.3.33":
@@ -1156,16 +1182,16 @@
resolved "https://registry.yarnpkg.com/@firebase/remote-config-types/-/remote-config-types-0.1.8.tgz#0c8d8a839621230053ba55704b5d1145bfe54daa"
integrity sha512-K12IBHO7OD4gCW0FEqZL9zMqVAfS4+joC4YIn3bHezZfu3RL+Bw1wCb0cAD7RfDPcQxWJjxOHpce4YhuqSxPFA==
-"@firebase/remote-config@0.1.17":
- version "0.1.17"
- resolved "https://registry.yarnpkg.com/@firebase/remote-config/-/remote-config-0.1.17.tgz#ac4a8c9def48d404bdfe72cdf47199360cd44d01"
- integrity sha512-jIRHXih0krVTNGYMewFVIaX8WPE1iS06fV4oMMHCCSSforGodv535uVZZ41Il29Q+22zOcyJvahoc990V0cFoA==
+"@firebase/remote-config@0.1.18":
+ version "0.1.18"
+ resolved "https://registry.yarnpkg.com/@firebase/remote-config/-/remote-config-0.1.18.tgz#2fb1f7b7f95b263824f5818470c27fb9854d4ac8"
+ integrity sha512-ufbhnP3O6bRYs74jFIyYZ0dPsv/PsMSmGs669Os5SkEdfjEWX8hnhssfuZCg5VfJQiCrqoSQD/KfPGACmeBcbQ==
dependencies:
- "@firebase/component" "0.1.8"
- "@firebase/installations" "0.4.6"
- "@firebase/logger" "0.2.0"
+ "@firebase/component" "0.1.9"
+ "@firebase/installations" "0.4.7"
+ "@firebase/logger" "0.2.1"
"@firebase/remote-config-types" "0.1.8"
- "@firebase/util" "0.2.43"
+ "@firebase/util" "0.2.44"
tslib "1.11.1"
"@firebase/storage-types@0.3.11":
@@ -1173,14 +1199,14 @@
resolved "https://registry.yarnpkg.com/@firebase/storage-types/-/storage-types-0.3.11.tgz#98f6ced5460502ab12778ce71d4dc9bf0ab7f2ee"
integrity sha512-EMOo5aeiJIa8eQ/VqjIa/DYlDcEJX1V84FOxmLfNWZIlmCSvcqx9E9mcNlOnoUB4iePqQjTMQRtKlIBvvEVhVg==
-"@firebase/storage@0.3.30":
- version "0.3.30"
- resolved "https://registry.yarnpkg.com/@firebase/storage/-/storage-0.3.30.tgz#195f6bd9b526710b7446e430b71dfb82cf8b35cc"
- integrity sha512-wapt4+NiEqTiLKPpsy+XbdLTN99pkqjf46Z7zqeS+vh+61cJsUT8M7YBfBb0ZN+dY6gnI5QNzviiKpykhJQbVA==
+"@firebase/storage@0.3.31":
+ version "0.3.31"
+ resolved "https://registry.yarnpkg.com/@firebase/storage/-/storage-0.3.31.tgz#399d8aab883be28ac73c792e6c34b44ba8ee498b"
+ integrity sha512-b5rwcMa89mFnKDlhFmGRC0QFpOgqGoNVn4klJDvSnpRGmwOcByQXoos8w1IWP0DW+EWhHcafy7DvUHFlr70onw==
dependencies:
- "@firebase/component" "0.1.8"
+ "@firebase/component" "0.1.9"
"@firebase/storage-types" "0.3.11"
- "@firebase/util" "0.2.43"
+ "@firebase/util" "0.2.44"
tslib "1.11.1"
"@firebase/util@0.2.43":
@@ -1190,6 +1216,13 @@
dependencies:
tslib "1.11.1"
+"@firebase/util@0.2.44":
+ version "0.2.44"
+ resolved "https://registry.yarnpkg.com/@firebase/util/-/util-0.2.44.tgz#bdf031f2714761ed6062ba2c32edca9113a339af"
+ integrity sha512-yWnFdeuz7P0QC4oC77JyPdAQ/rTGPDfhHcR5WsoMsKBBHTyqEhaKWL9HeRird+p3AL9M4++ep0FYFNd1UKU3Wg==
+ dependencies:
+ tslib "1.11.1"
+
"@firebase/webchannel-wrapper@0.2.38":
version "0.2.38"
resolved "https://registry.yarnpkg.com/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.2.38.tgz#1f0602cd73f7402ffc4d6116c811dfbb652caa73"
@@ -5229,25 +5262,25 @@ firebase-tools@^8.0.0:
winston "^3.0.0"
ws "^7.2.3"
-firebase@^7.13.1:
- version "7.13.1"
- resolved "https://registry.yarnpkg.com/firebase/-/firebase-7.13.1.tgz#f9c05ef60341cf8f7796ad7b8f3181b31be6dcf5"
- integrity sha512-v4Z7Wioy/7LMC8RJn1jNlOaUqLK7bUEva+Uf+qrbtFd7hM2rWLW+0IqHZBwPrQ5tI604uHj7IlhqAZEjm38AMw==
+firebase@^7.13.2:
+ version "7.13.2"
+ resolved "https://registry.yarnpkg.com/firebase/-/firebase-7.13.2.tgz#4a4af2b76514aecb2c4f7fb312fff7b828ab52bc"
+ integrity sha512-23auhESlkyW0q59BB1e9dVuK/eITLsQvzz2J4ImVDfHDIF4JnjNpVnoKgBsj1oOYtSnRE5QppJ8kZR3q9rIPaQ==
dependencies:
- "@firebase/analytics" "0.3.0"
- "@firebase/app" "0.6.0"
+ "@firebase/analytics" "0.3.1"
+ "@firebase/app" "0.6.1"
"@firebase/app-types" "0.6.0"
- "@firebase/auth" "0.14.1"
- "@firebase/database" "0.5.24"
- "@firebase/firestore" "1.13.0"
- "@firebase/functions" "0.4.38"
- "@firebase/installations" "0.4.6"
- "@firebase/messaging" "0.6.10"
- "@firebase/performance" "0.2.36"
+ "@firebase/auth" "0.14.2"
+ "@firebase/database" "0.5.25"
+ "@firebase/firestore" "1.13.1"
+ "@firebase/functions" "0.4.39"
+ "@firebase/installations" "0.4.7"
+ "@firebase/messaging" "0.6.11"
+ "@firebase/performance" "0.2.37"
"@firebase/polyfill" "0.3.33"
- "@firebase/remote-config" "0.1.17"
- "@firebase/storage" "0.3.30"
- "@firebase/util" "0.2.43"
+ "@firebase/remote-config" "0.1.18"
+ "@firebase/storage" "0.3.31"
+ "@firebase/util" "0.2.44"
flat-arguments@^1.0.0:
version "1.0.2"
@@ -9578,6 +9611,11 @@ proxy-addr@~2.0.5:
forwarded "~0.1.2"
ipaddr.js "1.9.1"
+proxy-polyfill@^0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/proxy-polyfill/-/proxy-polyfill-0.3.1.tgz#163d5283cf928dd8ddb5c5e88528e4ccd233496f"
+ integrity sha512-jywE1NIksgIGqZc4uF0QLbXGz2RcHQobsCkAW+8F0nr/6agap+TWksEAKyLnIBafPD88HT9qZR2ec0oomHdjcQ==
+
prr@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
diff --git a/src/analytics/analytics.module.ts b/src/analytics/analytics.module.ts
index a1e470a6f..3c104d5d3 100644
--- a/src/analytics/analytics.module.ts
+++ b/src/analytics/analytics.module.ts
@@ -1,5 +1,5 @@
import { NgModule, Optional } from '@angular/core';
-import { UserTrackingService, ScreenTrackingService } from './analytics.service';
+import { ScreenTrackingService, UserTrackingService } from './analytics.service';
import { AngularFireAnalytics } from './analytics';
@NgModule({
@@ -12,6 +12,7 @@ export class AngularFireAnalyticsModule {
@Optional() userTracking: UserTrackingService
) {
// calling anything on analytics will eagerly load the SDK
+ // tslint:disable-next-line:no-unused-expression
analytics.app;
}
}
diff --git a/src/analytics/analytics.service.ts b/src/analytics/analytics.service.ts
index c7df44ac6..ea00a572b 100644
--- a/src/analytics/analytics.service.ts
+++ b/src/analytics/analytics.service.ts
@@ -1,7 +1,17 @@
-import { Injectable, Optional, NgZone, OnDestroy, ComponentFactoryResolver, Inject, PLATFORM_ID, Injector, NgModuleFactory } from '@angular/core';
-import { Subscription, from, Observable, of } from 'rxjs';
-import { filter, withLatestFrom, switchMap, map, tap, pairwise, startWith, groupBy, mergeMap, observeOn } from 'rxjs/operators';
-import { Router, NavigationEnd, ActivationEnd, ROUTES } from '@angular/router';
+import {
+ ComponentFactoryResolver,
+ Inject,
+ Injectable,
+ Injector,
+ NgModuleFactory,
+ NgZone,
+ OnDestroy,
+ Optional,
+ PLATFORM_ID
+} from '@angular/core';
+import { from, Observable, of, Subscription } from 'rxjs';
+import { filter, groupBy, map, mergeMap, observeOn, pairwise, startWith, switchMap, tap, withLatestFrom } from 'rxjs/operators';
+import { ActivationEnd, NavigationEnd, Router, ROUTES } from '@angular/router';
import { ɵAngularFireSchedulers } from '@angular/fire';
import { AngularFireAnalytics, DEBUG_MODE } from './analytics';
import { User } from 'firebase/app';
@@ -10,7 +20,7 @@ import { isPlatformBrowser, isPlatformServer } from '@angular/common';
const FIREBASE_EVENT_ORIGIN_KEY = 'firebase_event_origin';
const FIREBASE_PREVIOUS_SCREEN_CLASS_KEY = 'firebase_previous_class';
-const FIREBASE_PREVIOUS_SCREEN_INSTANCE_ID_KEY = 'firebase_previous_id';
+const FIREBASE_PREVIOUS_SCREEN_INSTANCE_ID_KEY = 'firebase_previous_id';
const FIREBASE_PREVIOUS_SCREEN_NAME_KEY = 'firebase_previous_screen';
const FIREBASE_SCREEN_CLASS_KEY = 'firebase_screen_class';
const FIREBASE_SCREEN_INSTANCE_ID_KEY = 'firebase_screen_id';
@@ -29,178 +39,191 @@ const SCREEN_INSTANCE_DELIMITER = '#';
const ANNOTATIONS = '__annotations__';
+
+// this is an INT64 in iOS/Android but use INT32 cause javascript
+let nextScreenInstanceID = Math.floor(Math.random() * (2 ** 32 - 1)) - 2 ** 31;
+
+const knownScreenInstanceIDs: { [key: string]: number } = {};
+
+const getScreenInstanceID = (params: { [key: string]: any }) => {
+ // unique the screen class against the outlet name
+ const screenInstanceKey = [
+ params[SCREEN_CLASS_KEY],
+ params[OUTLET_KEY]
+ ].join(SCREEN_INSTANCE_DELIMITER);
+ if (knownScreenInstanceIDs.hasOwnProperty(screenInstanceKey)) {
+ return knownScreenInstanceIDs[screenInstanceKey];
+ } else {
+ const ret = nextScreenInstanceID++;
+ knownScreenInstanceIDs[screenInstanceKey] = ret;
+ return ret;
+ }
+};
+
@Injectable({
- providedIn: 'any'
+ providedIn: 'any'
})
export class ScreenTrackingService implements OnDestroy {
- private disposable: Subscription|undefined;
-
- constructor(
- analytics: AngularFireAnalytics,
- @Optional() router:Router,
- @Optional() title:Title,
- componentFactoryResolver: ComponentFactoryResolver,
- @Inject(PLATFORM_ID) platformId:Object,
- @Optional() @Inject(DEBUG_MODE) debugModeEnabled:boolean|null,
- zone: NgZone,
- injector: Injector
- ) {
- if (!router || !isPlatformBrowser(platformId)) { return this }
- zone.runOutsideAngular(() => {
- const activationEndEvents = router.events.pipe(filter(e => e instanceof ActivationEnd));
- const navigationEndEvents = router.events.pipe(filter(e => e instanceof NavigationEnd));
- this.disposable = navigationEndEvents.pipe(
- withLatestFrom(activationEndEvents),
- switchMap(([navigationEnd, activationEnd]) => {
- // SEMVER: start using optional chains and nullish coalescing once we support newer typescript
- const page_path = navigationEnd.url;
- const screen_name = activationEnd.snapshot.routeConfig && activationEnd.snapshot.routeConfig.path || page_path;
- const params = {
- [SCREEN_NAME_KEY]: screen_name,
- [PAGE_PATH_KEY]: page_path,
- [FIREBASE_EVENT_ORIGIN_KEY]: EVENT_ORIGIN_AUTO,
- [FIREBASE_SCREEN_NAME_KEY]: screen_name,
- [OUTLET_KEY]: activationEnd.snapshot.outlet
- };
- if (title) {
- params[PAGE_TITLE_KEY] = title.getTitle()
- }
- const component = activationEnd.snapshot.component;
- const routeConfig = activationEnd.snapshot.routeConfig;
- const loadChildren = routeConfig && routeConfig.loadChildren;
- // TODO figure out how to handle minification
- if (typeof loadChildren === "string") {
- // SEMVER: this is the older lazy load style "./path#ClassName", drop this when we drop old ng
- // TODO is it worth seeing if I can look up the component factory selector from the module name?
- // it's lazy so it's not registered with componentFactoryResolver yet... seems a pain for a depreciated style
- return of({...params, [SCREEN_CLASS_KEY]: loadChildren.split('#')[1]});
- } else if (typeof component === 'string') {
- return of({...params, [SCREEN_CLASS_KEY]: component });
- } else if (component) {
- const componentFactory = componentFactoryResolver.resolveComponentFactory(component);
- return of({...params, [SCREEN_CLASS_KEY]: componentFactory.selector });
- } else if (loadChildren) {
- const loadedChildren = loadChildren();
- var loadedChildren$: Observable = (loadedChildren instanceof Observable) ? loadedChildren : from(Promise.resolve(loadedChildren));
- return loadedChildren$.pipe(
- map(lazyModule => {
- if (lazyModule instanceof NgModuleFactory) {
- // AOT create an injector
- const moduleRef = lazyModule.create(injector);
- // INVESTIGATE is this the right way to get at the matching route?
- const routes = moduleRef.injector.get(ROUTES);
- const component = routes[0][0].component; // should i just be grabbing 0-0 here?
- try {
- const componentFactory = moduleRef.componentFactoryResolver.resolveComponentFactory(component!);
- return {...params, [SCREEN_CLASS_KEY]: componentFactory.selector};
- } catch(_) {
- return {...params, [SCREEN_CLASS_KEY]: DEFAULT_SCREEN_CLASS};
- }
- } else {
- // JIT look at the annotations
- // INVESTIGATE are there public APIs for this stuff?
- const declarations = [].concat.apply([], (lazyModule[ANNOTATIONS] || []).map((f:any) => f.declarations));
- const selectors = [].concat.apply([], declarations.map((c:any) => (c[ANNOTATIONS] || []).map((f:any) => f.selector)));
- // should I just be grabbing the selector like this or should i match against the route component?
- // const routerModule = lazyModule.ngInjectorDef.imports.find(i => i.ngModule && ....);
- // const route = routerModule.providers[0].find(p => p.provide == ROUTES).useValue[0];
- return {...params, [SCREEN_CLASS_KEY]: selectors[0] || DEFAULT_SCREEN_CLASS};
- }
- })
- );
- } else {
- return of({...params, [SCREEN_CLASS_KEY]: DEFAULT_SCREEN_CLASS});
- }
- }),
- map(params => ({
- [FIREBASE_SCREEN_CLASS_KEY]: params[SCREEN_CLASS_KEY],
- [FIREBASE_SCREEN_INSTANCE_ID_KEY]: getScreenInstanceID(params),
- ...params
- })),
- tap(params => {
- // TODO perhaps I can be smarter about this, bubble events up to the nearest outlet?
- if (params[OUTLET_KEY] == NG_PRIMARY_OUTLET) {
- analytics.setCurrentScreen(params[SCREEN_NAME_KEY]);
- analytics.updateConfig({
- [PAGE_PATH_KEY]: params[PAGE_PATH_KEY],
- [SCREEN_CLASS_KEY]: params[SCREEN_CLASS_KEY]
- });
- if (title) {
- analytics.updateConfig({ [PAGE_TITLE_KEY]: params[PAGE_TITLE_KEY] })
- }
- }
- }),
- groupBy(params => params[OUTLET_KEY]),
- mergeMap(group => group.pipe(startWith(undefined), pairwise())),
- map(([prior, current]) => prior ? {
- [FIREBASE_PREVIOUS_SCREEN_CLASS_KEY]: prior[SCREEN_CLASS_KEY],
- [FIREBASE_PREVIOUS_SCREEN_NAME_KEY]: prior[SCREEN_NAME_KEY],
- [FIREBASE_PREVIOUS_SCREEN_INSTANCE_ID_KEY]: prior[FIREBASE_SCREEN_INSTANCE_ID_KEY],
- ...current!
- } : current!),
- tap(params => debugModeEnabled && console.info(SCREEN_VIEW_EVENT, params)),
- tap(params => zone.runOutsideAngular(() => analytics.logEvent(SCREEN_VIEW_EVENT, params)))
- ).subscribe();
- });
+ private disposable: Subscription | undefined;
+
+ constructor(
+ analytics: AngularFireAnalytics,
+ @Optional() router: Router,
+ @Optional() title: Title,
+ componentFactoryResolver: ComponentFactoryResolver,
+ // tslint:disable-next-line:ban-types
+ @Inject(PLATFORM_ID) platformId: Object,
+ @Optional() @Inject(DEBUG_MODE) debugModeEnabled: boolean | null,
+ zone: NgZone,
+ injector: Injector
+ ) {
+ if (!router || !isPlatformBrowser(platformId)) {
+ return this;
}
-
- ngOnDestroy() {
- if (this.disposable) { this.disposable.unsubscribe(); }
+ zone.runOutsideAngular(() => {
+ const activationEndEvents = router.events.pipe(filter(e => e instanceof ActivationEnd));
+ const navigationEndEvents = router.events.pipe(filter(e => e instanceof NavigationEnd));
+ this.disposable = navigationEndEvents.pipe(
+ withLatestFrom(activationEndEvents),
+ switchMap(([navigationEnd, activationEnd]) => {
+ // SEMVER: start using optional chains and nullish coalescing once we support newer typescript
+ const pagePath = navigationEnd.url;
+ const screenName = activationEnd.snapshot.routeConfig && activationEnd.snapshot.routeConfig.path || pagePath;
+ const params = {
+ [SCREEN_NAME_KEY]: screenName,
+ [PAGE_PATH_KEY]: pagePath,
+ [FIREBASE_EVENT_ORIGIN_KEY]: EVENT_ORIGIN_AUTO,
+ [FIREBASE_SCREEN_NAME_KEY]: screenName,
+ [OUTLET_KEY]: activationEnd.snapshot.outlet
+ };
+ if (title) {
+ params[PAGE_TITLE_KEY] = title.getTitle();
+ }
+ const component = activationEnd.snapshot.component;
+ const routeConfig = activationEnd.snapshot.routeConfig;
+ const loadChildren = routeConfig && routeConfig.loadChildren;
+ // TODO figure out how to handle minification
+ if (typeof loadChildren === 'string') {
+ // SEMVER: this is the older lazy load style "./path#ClassName", drop this when we drop old ng
+ // TODO is it worth seeing if I can look up the component factory selector from the module name?
+ // it's lazy so it's not registered with componentFactoryResolver yet... seems a pain for a depreciated style
+ return of({ ...params, [SCREEN_CLASS_KEY]: loadChildren.split('#')[1] });
+ } else if (typeof component === 'string') {
+ return of({ ...params, [SCREEN_CLASS_KEY]: component });
+ } else if (component) {
+ const componentFactory = componentFactoryResolver.resolveComponentFactory(component);
+ return of({ ...params, [SCREEN_CLASS_KEY]: componentFactory.selector });
+ } else if (loadChildren) {
+ const loadedChildren = loadChildren();
+ const loadedChildren$: Observable = (loadedChildren instanceof Observable) ?
+ loadedChildren :
+ from(Promise.resolve(loadedChildren));
+ return loadedChildren$.pipe(
+ map(lazyModule => {
+ if (lazyModule instanceof NgModuleFactory) {
+ // AOT create an injector
+ const moduleRef = lazyModule.create(injector);
+ // INVESTIGATE is this the right way to get at the matching route?
+ const routes = moduleRef.injector.get(ROUTES);
+ const component = routes[0][0].component; // should i just be grabbing 0-0 here?
+ try {
+ const componentFactory = moduleRef.componentFactoryResolver.resolveComponentFactory(component);
+ return { ...params, [SCREEN_CLASS_KEY]: componentFactory.selector };
+ } catch (_) {
+ return { ...params, [SCREEN_CLASS_KEY]: DEFAULT_SCREEN_CLASS };
+ }
+ } else {
+ // JIT look at the annotations
+ // INVESTIGATE are there public APIs for this stuff?
+ const declarations = [].concat.apply([], (lazyModule[ANNOTATIONS] || []).map((f: any) => f.declarations));
+ const selectors = [].concat.apply([], declarations.map((c: any) => (c[ANNOTATIONS] || []).map((f: any) => f.selector)));
+ // should I just be grabbing the selector like this or should i match against the route component?
+ // const routerModule = lazyModule.ngInjectorDef.imports.find(i => i.ngModule && ....);
+ // const route = routerModule.providers[0].find(p => p.provide == ROUTES).useValue[0];
+ return { ...params, [SCREEN_CLASS_KEY]: selectors[0] || DEFAULT_SCREEN_CLASS };
+ }
+ })
+ );
+ } else {
+ return of({ ...params, [SCREEN_CLASS_KEY]: DEFAULT_SCREEN_CLASS });
+ }
+ }),
+ map(params => ({
+ [FIREBASE_SCREEN_CLASS_KEY]: params[SCREEN_CLASS_KEY],
+ [FIREBASE_SCREEN_INSTANCE_ID_KEY]: getScreenInstanceID(params),
+ ...params
+ })),
+ tap(params => {
+ // TODO perhaps I can be smarter about this, bubble events up to the nearest outlet?
+ if (params[OUTLET_KEY] === NG_PRIMARY_OUTLET) {
+ analytics.setCurrentScreen(params[SCREEN_NAME_KEY]);
+ analytics.updateConfig({
+ [PAGE_PATH_KEY]: params[PAGE_PATH_KEY],
+ [SCREEN_CLASS_KEY]: params[SCREEN_CLASS_KEY]
+ });
+ if (title) {
+ analytics.updateConfig({ [PAGE_TITLE_KEY]: params[PAGE_TITLE_KEY] });
+ }
+ }
+ }),
+ groupBy(params => params[OUTLET_KEY]),
+ // tslint:disable-next-line
+ mergeMap(group => group.pipe(startWith(undefined), pairwise())),
+ map(([prior, current]) => prior ? {
+ [FIREBASE_PREVIOUS_SCREEN_CLASS_KEY]: prior[SCREEN_CLASS_KEY],
+ [FIREBASE_PREVIOUS_SCREEN_NAME_KEY]: prior[SCREEN_NAME_KEY],
+ [FIREBASE_PREVIOUS_SCREEN_INSTANCE_ID_KEY]: prior[FIREBASE_SCREEN_INSTANCE_ID_KEY],
+ ...current
+ } : current),
+ // tslint:disable-next-line:no-console
+ tap(params => debugModeEnabled && console.info(SCREEN_VIEW_EVENT, params)),
+ tap(params => zone.runOutsideAngular(() => analytics.logEvent(SCREEN_VIEW_EVENT, params)))
+ ).subscribe();
+ });
+ }
+
+ ngOnDestroy() {
+ if (this.disposable) {
+ this.disposable.unsubscribe();
}
-
+ }
+
}
@Injectable({
- providedIn: 'any'
+ providedIn: 'any'
})
export class UserTrackingService implements OnDestroy {
- private disposable: Subscription|undefined;
-
- // TODO a user properties injector
- constructor(
- analytics: AngularFireAnalytics,
- zone: NgZone,
- @Inject(PLATFORM_ID) platformId:Object
- ) {
- const schedulers = new ɵAngularFireSchedulers(zone);
-
- if (!isPlatformServer(platformId)) {
- zone.runOutsideAngular(() => {
- // @ts-ignore zap the import in the UMD
- this.disposable = from(import('firebase/auth')).pipe(
- observeOn(schedulers.outsideAngular),
- switchMap(() => analytics.app),
- map(app => app.auth()),
- switchMap(auth => new Observable(auth.onAuthStateChanged.bind(auth))),
- switchMap(user => analytics.setUserId(user ? user.uid : null!))
- ).subscribe();
- });
- }
- }
+ private disposable: Subscription | undefined;
+
+ // TODO a user properties injector
+ constructor(
+ analytics: AngularFireAnalytics,
+ zone: NgZone,
+ // tslint:disable-next-line:ban-types
+ @Inject(PLATFORM_ID) platformId: Object
+ ) {
+ const schedulers = new ɵAngularFireSchedulers(zone);
- ngOnDestroy() {
- if (this.disposable) { this.disposable.unsubscribe(); }
+ if (!isPlatformServer(platformId)) {
+ zone.runOutsideAngular(() => {
+ // @ts-ignore zap the import in the UMD
+ this.disposable = from(import('firebase/auth')).pipe(
+ observeOn(schedulers.outsideAngular),
+ switchMap(() => analytics.app),
+ map(app => app.auth()),
+ switchMap(auth => new Observable(auth.onAuthStateChanged.bind(auth))),
+ switchMap(user => analytics.setUserId(user ? user.uid : null))
+ ).subscribe();
+ });
}
-}
+ }
-// this is an INT64 in iOS/Android but use INT32 cause javascript
-let nextScreenInstanceID = Math.floor(Math.random() * (2**32 - 1)) - 2**31;
-
-const knownScreenInstanceIDs: {[key:string]: number} = {};
-
-const getScreenInstanceID = (params:{[key:string]: any}) => {
- // unique the screen class against the outlet name
- const screenInstanceKey = [
- params[SCREEN_CLASS_KEY],
- params[OUTLET_KEY]
- ].join(SCREEN_INSTANCE_DELIMITER);
- if (knownScreenInstanceIDs.hasOwnProperty(screenInstanceKey)) {
- return knownScreenInstanceIDs[screenInstanceKey];
- } else {
- const ret = nextScreenInstanceID++;
- knownScreenInstanceIDs[screenInstanceKey] = ret;
- return ret;
+ ngOnDestroy() {
+ if (this.disposable) {
+ this.disposable.unsubscribe();
}
-}
\ No newline at end of file
+ }
+}
diff --git a/src/analytics/analytics.spec.ts b/src/analytics/analytics.spec.ts
index b956dc76d..9804f993a 100644
--- a/src/analytics/analytics.spec.ts
+++ b/src/analytics/analytics.spec.ts
@@ -1,6 +1,6 @@
-import { TestBed, inject } from '@angular/core/testing';
-import { FirebaseApp, FIREBASE_OPTIONS, AngularFireModule, FIREBASE_APP_NAME } from '@angular/fire';
-import { AngularFireAnalytics, AngularFireAnalyticsModule, COLLECTION_ENABLED, APP_VERSION, APP_NAME } from './public_api';
+import { TestBed } from '@angular/core/testing';
+import { AngularFireModule, FirebaseApp } from '@angular/fire';
+import { AngularFireAnalytics, AngularFireAnalyticsModule } from './public_api';
import { COMMON_CONFIG } from '../test-config';
import { rando } from '../firestore/utils.spec';
@@ -16,10 +16,9 @@ describe('AngularFireAnalytics', () => {
AngularFireAnalyticsModule
]
});
- inject([FirebaseApp, AngularFireAnalytics], (app_: FirebaseApp, _analytics: AngularFireAnalytics) => {
- app = app_;
- analytics = _analytics;
- })();
+
+ app = TestBed.inject(FirebaseApp);
+ analytics = TestBed.inject(AngularFireAnalytics);
});
afterEach(() => {
@@ -34,4 +33,4 @@ describe('AngularFireAnalytics', () => {
expect(analytics.app).toBeDefined();
});
-});
\ No newline at end of file
+});
diff --git a/src/analytics/analytics.ts b/src/analytics/analytics.ts
index 9862f32f5..3e6977cc9 100644
--- a/src/analytics/analytics.ts
+++ b/src/analytics/analytics.ts
@@ -1,11 +1,22 @@
-import { Injectable, Inject, Optional, NgZone, InjectionToken, PLATFORM_ID } from '@angular/core';
-import { of, empty, Observable } from 'rxjs';
+import { Inject, Injectable, InjectionToken, NgZone, Optional, PLATFORM_ID } from '@angular/core';
+import { EMPTY, Observable, of } from 'rxjs';
import { isPlatformBrowser } from '@angular/common';
import { map, tap, shareReplay, switchMap, observeOn } from 'rxjs/operators';
-import { FirebaseAppConfig, FirebaseOptions, ɵAngularFireSchedulers, ɵlazySDKProxy, FIREBASE_OPTIONS, FIREBASE_APP_NAME, ɵfirebaseAppFactory, ɵPromiseProxy } from '@angular/fire';
-import { analytics } from 'firebase';
+import {
+ FirebaseAppConfig,
+ FirebaseOptions,
+ ɵAngularFireSchedulers,
+ ɵlazySDKProxy,
+ FIREBASE_OPTIONS,
+ FIREBASE_APP_NAME,
+ ɵfirebaseAppFactory,
+ ɵPromiseProxy
+} from '@angular/fire';
+import { analytics } from 'firebase/app';
-export interface Config {[key:string]: any};
+export interface Config {
+ [key: string]: any;
+}
export const COLLECTION_ENABLED = new InjectionToken('angularfire2.analytics.analyticsCollectionEnabled');
export const APP_VERSION = new InjectionToken('angularfire2.analytics.appVersion');
@@ -21,11 +32,12 @@ const GTAG_CONFIG_COMMAND = 'config';
const GTAG_FUNCTION_NAME = 'gtag';
const DATA_LAYER_NAME = 'dataLayer';
-export interface AngularFireAnalytics extends ɵPromiseProxy {};
+export interface AngularFireAnalytics extends ɵPromiseProxy {
+}
let gtag: (...args: any[]) => void;
let analyticsInitialized: Promise;
-const analyticsInstanceCache: {[key:string]: Observable} = {};
+const analyticsInstanceCache: { [key: string]: Observable } = {};
@Injectable({
providedIn: 'any'
@@ -35,34 +47,40 @@ export class AngularFireAnalytics {
async updateConfig(config: Config) {
await analyticsInitialized;
gtag(GTAG_CONFIG_COMMAND, this.options[ANALYTICS_ID_FIELD], { ...config, update: true });
- };
+ }
constructor(
- @Inject(FIREBASE_OPTIONS) private options:FirebaseOptions,
- @Optional() @Inject(FIREBASE_APP_NAME) nameOrConfig:string|FirebaseAppConfig|null|undefined,
- @Optional() @Inject(COLLECTION_ENABLED) analyticsCollectionEnabled:boolean|null,
- @Optional() @Inject(APP_VERSION) providedAppVersion:string|null,
- @Optional() @Inject(APP_NAME) providedAppName:string|null,
- @Optional() @Inject(DEBUG_MODE) debugModeEnabled:boolean|null,
- @Optional() @Inject(CONFIG) providedConfig:Config|null,
- @Inject(PLATFORM_ID) platformId:Object,
+ @Inject(FIREBASE_OPTIONS) private options: FirebaseOptions,
+ @Optional() @Inject(FIREBASE_APP_NAME) nameOrConfig: string | FirebaseAppConfig | null | undefined,
+ @Optional() @Inject(COLLECTION_ENABLED) analyticsCollectionEnabled: boolean | null,
+ @Optional() @Inject(APP_VERSION) providedAppVersion: string | null,
+ @Optional() @Inject(APP_NAME) providedAppName: string | null,
+ @Optional() @Inject(DEBUG_MODE) debugModeEnabled: boolean | null,
+ @Optional() @Inject(CONFIG) providedConfig: Config | null,
+ // tslint:disable-next-line:ban-types
+ @Inject(PLATFORM_ID) platformId: Object,
zone: NgZone
) {
if (!analyticsInitialized) {
if (isPlatformBrowser(platformId)) {
- gtag = window[GTAG_FUNCTION_NAME] || function() { window[DATA_LAYER_NAME].push(arguments) };
+ gtag = window[GTAG_FUNCTION_NAME] || ((...args: any[]) => {
+ window[DATA_LAYER_NAME].push(args);
+ });
window[DATA_LAYER_NAME] = window[DATA_LAYER_NAME] || [];
analyticsInitialized = zone.runOutsideAngular(() =>
new Promise(resolve => {
window[GTAG_FUNCTION_NAME] = (...args: any[]) => {
- if (args[0] == 'js') { resolve() }
+ if (args[0] === 'js') {
+ resolve();
+ }
gtag(...args);
- }
+ };
})
);
} else {
- gtag = () => {};
+ gtag = () => {
+ };
analyticsInitialized = Promise.resolve();
}
}
@@ -71,24 +89,34 @@ export class AngularFireAnalytics {
if (!analytics) {
analytics = of(undefined).pipe(
observeOn(new ɵAngularFireSchedulers(zone).outsideAngular),
- switchMap(() => isPlatformBrowser(platformId) ? import('firebase/analytics') : empty()),
+ switchMap(() => isPlatformBrowser(platformId) ? import('firebase/analytics') : EMPTY),
map(() => ɵfirebaseAppFactory(options, zone, nameOrConfig)),
map(app => app.analytics()),
tap(analytics => {
- if (analyticsCollectionEnabled === false) { analytics.setAnalyticsCollectionEnabled(false) }
+ if (analyticsCollectionEnabled === false) {
+ analytics.setAnalyticsCollectionEnabled(false);
+ }
}),
- shareReplay({ bufferSize: 1, refCount: false }),
+ shareReplay({ bufferSize: 1, refCount: false })
);
analyticsInstanceCache[options[ANALYTICS_ID_FIELD]] = analytics;
}
- if (providedConfig) { this.updateConfig(providedConfig) }
- if (providedAppName) { this.updateConfig({ [APP_NAME_KEY]: providedAppName }) }
- if (providedAppVersion) { this.updateConfig({ [APP_VERSION_KEY]: providedAppVersion }) }
- if (debugModeEnabled) { this.updateConfig({ [DEBUG_MODE_KEY]: 1 }) }
+ if (providedConfig) {
+ this.updateConfig(providedConfig);
+ }
+ if (providedAppName) {
+ this.updateConfig({ [APP_NAME_KEY]: providedAppName });
+ }
+ if (providedAppVersion) {
+ this.updateConfig({ [APP_VERSION_KEY]: providedAppVersion });
+ }
+ if (debugModeEnabled) {
+ this.updateConfig({ [DEBUG_MODE_KEY]: 1 });
+ }
return ɵlazySDKProxy(this, analytics, zone);
}
-}
\ No newline at end of file
+}
diff --git a/src/auth-guard/auth-guard.spec.ts b/src/auth-guard/auth-guard.spec.ts
index c930b24cf..7c3ed2c45 100644
--- a/src/auth-guard/auth-guard.spec.ts
+++ b/src/auth-guard/auth-guard.spec.ts
@@ -1,39 +1,38 @@
-import { TestBed, inject } from '@angular/core/testing';
-import { FirebaseApp, AngularFireModule } from '@angular/fire';
+import { TestBed } from '@angular/core/testing';
+import { AngularFireModule, FirebaseApp } from '@angular/fire';
import { COMMON_CONFIG } from '../test-config';
-import { AngularFireAuthGuardModule, AngularFireAuthGuard } from './public_api';
-import { RouterModule, Router } from '@angular/router';
+import { AngularFireAuthGuard, AngularFireAuthGuardModule } from './public_api';
+import { Router, RouterModule } from '@angular/router';
import { APP_BASE_HREF } from '@angular/common';
import { rando } from '../firestore/utils.spec';
describe('AngularFireAuthGuard', () => {
- let app: FirebaseApp;
- let router: Router;
+ let app: FirebaseApp;
+ let router: Router;
- beforeEach(() => {
- TestBed.configureTestingModule({
- imports: [
- AngularFireModule.initializeApp(COMMON_CONFIG, rando()),
- AngularFireAuthGuardModule,
- RouterModule.forRoot([
- { path: 'a', redirectTo: '/', canActivate: [AngularFireAuthGuard] }
- ])
- ],
- providers: [
- { provide: APP_BASE_HREF, useValue: 'http://localhost:4200/' }
- ]
- });
- inject([FirebaseApp, Router], (app_: FirebaseApp, router_: Router) => {
- app = app_;
- router = router_;
- })();
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ AngularFireModule.initializeApp(COMMON_CONFIG, rando()),
+ AngularFireAuthGuardModule,
+ RouterModule.forRoot([
+ { path: 'a', redirectTo: '/', canActivate: [AngularFireAuthGuard] }
+ ])
+ ],
+ providers: [
+ { provide: APP_BASE_HREF, useValue: 'http://localhost:4200/' }
+ ]
});
- afterEach(done => {
- app.delete().then(done, done);
- });
-
- it('should be injectable', () => {
- expect(router).toBeTruthy();
- });
-});
\ No newline at end of file
+ app = TestBed.inject(FirebaseApp);
+ router = TestBed.inject(Router);
+ });
+
+ afterEach(done => {
+ app.delete().then(done, done);
+ });
+
+ it('should be injectable', () => {
+ expect(router).toBeTruthy();
+ });
+});
diff --git a/src/auth-guard/auth-guard.ts b/src/auth-guard/auth-guard.ts
index 93ebb13d4..f63aeb621 100644
--- a/src/auth-guard/auth-guard.ts
+++ b/src/auth-guard/auth-guard.ts
@@ -1,13 +1,23 @@
-import { Injectable, Inject, Optional, NgZone } from '@angular/core';
-import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
+import { Inject, Injectable, NgZone, Optional } from '@angular/core';
+import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
import { Observable, of, pipe, UnaryFunction } from 'rxjs';
-import { map, switchMap, take, observeOn, shareReplay } from 'rxjs/operators'
+import { map, observeOn, shareReplay, switchMap, take } from 'rxjs/operators';
import { User } from 'firebase/app';
-import { ɵAngularFireSchedulers, FirebaseOptions, FirebaseAppConfig, FIREBASE_OPTIONS, FIREBASE_APP_NAME, ɵfirebaseAppFactory } from '@angular/fire';
+import {
+ ɵAngularFireSchedulers,
+ FirebaseOptions,
+ FirebaseAppConfig,
+ FIREBASE_OPTIONS,
+ FIREBASE_APP_NAME,
+ ɵfirebaseAppFactory,
+ ɵkeepUnstableUntilFirstFactory
+} from '@angular/fire';
export type AuthPipeGenerator = (next: ActivatedRouteSnapshot, state: RouterStateSnapshot) => AuthPipe;
export type AuthPipe = UnaryFunction, Observable>;
+export const loggedIn: AuthPipe = map(user => !!user);
+
@Injectable({
providedIn: 'any'
})
@@ -16,22 +26,26 @@ export class AngularFireAuthGuard implements CanActivate {
authState: Observable;
constructor(
- @Inject(FIREBASE_OPTIONS) options:FirebaseOptions,
- @Optional() @Inject(FIREBASE_APP_NAME) nameOrConfig:string|FirebaseAppConfig|null|undefined,
+ @Inject(FIREBASE_OPTIONS) options: FirebaseOptions,
+ @Optional() @Inject(FIREBASE_APP_NAME) nameOrConfig: string|FirebaseAppConfig|null|undefined,
zone: NgZone,
private router: Router
) {
+
+ const schedulers = new ɵAngularFireSchedulers(zone);
+ const keepUnstableUntilFirst = ɵkeepUnstableUntilFirstFactory(schedulers);
+
const auth = of(undefined).pipe(
observeOn(new ɵAngularFireSchedulers(zone).outsideAngular),
switchMap(() => zone.runOutsideAngular(() => import('firebase/auth'))),
- observeOn(new ɵAngularFireSchedulers(zone).insideAngular),
map(() => ɵfirebaseAppFactory(options, zone, nameOrConfig)),
- map(app => app.auth()),
+ map(app => zone.runOutsideAngular(() => app.auth())),
shareReplay({ bufferSize: 1, refCount: false }),
);
this.authState = auth.pipe(
- switchMap(auth => new Observable(auth.onAuthStateChanged.bind(auth)))
+ switchMap(auth => new Observable(auth.onAuthStateChanged.bind(auth))),
+ keepUnstableUntilFirst
);
}
@@ -40,7 +54,7 @@ export class AngularFireAuthGuard implements CanActivate {
return this.authState.pipe(
take(1),
authPipeFactory(next, state),
- map(can => typeof can == "boolean" ? can : this.router.createUrlTree(can))
+ map(can => typeof can === 'boolean' ? can : this.router.createUrlTree(can as any[]))
);
}
@@ -50,11 +64,14 @@ export const canActivate = (pipe: AuthPipeGenerator) => ({
canActivate: [ AngularFireAuthGuard ], data: { authGuardPipe: pipe }
});
-export const loggedIn: AuthPipe = map(user => !!user);
+
export const isNotAnonymous: AuthPipe = map(user => !!user && !user.isAnonymous);
export const idTokenResult = switchMap((user: User|null) => user ? user.getIdTokenResult() : of(null));
export const emailVerified: AuthPipe = map(user => !!user && user.emailVerified);
export const customClaims = pipe(idTokenResult, map(idTokenResult => idTokenResult ? idTokenResult.claims : []));
-export const hasCustomClaim = (claim:string) => pipe(customClaims, map(claims => claims.hasOwnProperty(claim)));
-export const redirectUnauthorizedTo = (redirect: any[]) => pipe(loggedIn, map(loggedIn => loggedIn || redirect));
-export const redirectLoggedInTo = (redirect: any[]) => pipe(loggedIn, map(loggedIn => loggedIn && redirect || true));
+export const hasCustomClaim: (claim: string) => AuthPipe =
+ (claim) => pipe(customClaims, map(claims => claims.hasOwnProperty(claim)));
+export const redirectUnauthorizedTo: (redirect: any[]) => AuthPipe =
+ (redirect) => pipe(loggedIn, map(loggedIn => loggedIn || redirect));
+export const redirectLoggedInTo: (redirect: any[]) => AuthPipe =
+ (redirect) => pipe(loggedIn, map(loggedIn => loggedIn && redirect || true));
diff --git a/src/auth-guard/public_api.ts b/src/auth-guard/public_api.ts
index f939652ac..468fd9657 100644
--- a/src/auth-guard/public_api.ts
+++ b/src/auth-guard/public_api.ts
@@ -1,2 +1,2 @@
export * from './auth-guard';
-export * from './auth-guard.module';
\ No newline at end of file
+export * from './auth-guard.module';
diff --git a/src/auth/auth.spec.ts b/src/auth/auth.spec.ts
index a5e04572e..70aefc9e8 100644
--- a/src/auth/auth.spec.ts
+++ b/src/auth/auth.spec.ts
@@ -1,30 +1,20 @@
import { User } from 'firebase/app';
-import { Observable, Subject } from 'rxjs'
-import { TestBed, inject } from '@angular/core/testing';
-import { FirebaseApp, FIREBASE_OPTIONS, AngularFireModule, FIREBASE_APP_NAME } from '@angular/fire';
+import { Observable, Subject } from 'rxjs';
+import { TestBed } from '@angular/core/testing';
+import { AngularFireModule, FIREBASE_APP_NAME, FIREBASE_OPTIONS, FirebaseApp } from '@angular/fire';
import { AngularFireAuth, AngularFireAuthModule } from './public_api';
import { COMMON_CONFIG } from '../test-config';
-import { take, skip } from 'rxjs/operators';
import 'firebase/auth';
import { rando } from '../firestore/utils.spec';
-function authTake(auth: Observable, count: number): Observable {
- return take.call(auth, 1);
-}
-
-function authSkip(auth: Observable, count: number): Observable {
- return skip.call(auth, 1);
-}
-
-const firebaseUser = {
+const firebaseUser = {
uid: '12345',
providerData: [{ displayName: 'jeffbcrossyface' }]
-};
+} as User;
describe('AngularFireAuth', () => {
let app: FirebaseApp;
let afAuth: AngularFireAuth;
- let authSpy: jasmine.Spy;
let mockAuthState: Subject;
beforeEach(() => {
@@ -34,18 +24,17 @@ describe('AngularFireAuth', () => {
AngularFireAuthModule
]
});
- inject([FirebaseApp, AngularFireAuth], (app_: FirebaseApp, _auth: AngularFireAuth) => {
- app = app_;
- afAuth = _auth;
- })();
+
+ app = TestBed.inject(FirebaseApp);
+ afAuth = TestBed.inject(AngularFireAuth);
mockAuthState = new Subject();
- //@ts-ignore
+ // @ts-ignore
spyOn(afAuth, 'authState').and.returnValue(mockAuthState);
- //@ts-ignore
+ // @ts-ignore
spyOn(afAuth, 'idToken').and.returnValue(mockAuthState);
- (afAuth).authState = mockAuthState as Observable;
- (afAuth).idToken = mockAuthState as Observable;
+ (afAuth as any).authState = mockAuthState as Observable;
+ (afAuth as any).idToken = mockAuthState as Observable;
});
afterEach(() => {
@@ -55,16 +44,16 @@ describe('AngularFireAuth', () => {
describe('Zones', () => {
it('should call operators and subscriber in the same zone as when service was initialized', (done) => {
// Initialize the app outside of the zone, to mimick real life behavior.
- let ngZone = Zone.current.fork({
+ const ngZone = Zone.current.fork({
name: 'ngZone'
});
ngZone.run(() => {
const subs = [
- afAuth.authState.subscribe(user => {
+ afAuth.authState.subscribe(() => {
expect(Zone.current.name).toBe('ngZone');
done();
}, done.fail),
- afAuth.authState.subscribe(user => {
+ afAuth.authState.subscribe(() => {
expect(Zone.current.name).toBe('ngZone');
done();
}, done.fail)
@@ -87,36 +76,44 @@ describe('AngularFireAuth', () => {
let count = 0;
// Check that the first value is null and second is the auth user
- const subs = afAuth.authState.subscribe(user => {
- if (count === 0) {
- expect(user).toBe(null!);
- count = count + 1;
- mockAuthState.next(firebaseUser);
- } else {
- expect(user).toEqual(firebaseUser);
- subs.unsubscribe();
- done();
- }
- }, done, done.fail);
- mockAuthState.next(null!);
+ const subs = afAuth.authState.subscribe({
+ next: (user => {
+ if (count === 0) {
+ expect(user).toBe(null);
+ count = count + 1;
+ mockAuthState.next(firebaseUser);
+ } else {
+ expect(user).toEqual(firebaseUser);
+ subs.unsubscribe();
+ done();
+ }
+ }),
+ error: done,
+ complete: done.fail
+ });
+ mockAuthState.next(null);
});
it('should emit auth updates through idToken', (done: any) => {
let count = 0;
// Check that the first value is null and second is the auth user
- const subs = afAuth.idToken.subscribe(user => {
- if (count === 0) {
- expect(user).toBe(null!);
- count = count + 1;
- mockAuthState.next(firebaseUser);
- } else {
- expect(user).toEqual(firebaseUser);
- subs.unsubscribe();
- done();
- }
- }, done, done.fail);
- mockAuthState.next(null!);
+ const subs = afAuth.idToken.subscribe({
+ next: user => {
+ if (count === 0) {
+ expect(user).toBe(null);
+ count = count + 1;
+ mockAuthState.next(firebaseUser);
+ } else {
+ expect(user as any).toEqual(firebaseUser);
+ subs.unsubscribe();
+ done();
+ }
+ },
+ error: done,
+ complete: done.fail
+ });
+ mockAuthState.next(null);
});
});
@@ -138,10 +135,9 @@ describe('AngularFireAuth with different app', () => {
{ provide: FIREBASE_OPTIONS, useValue: COMMON_CONFIG }
]
});
- inject([FirebaseApp, AngularFireAuth], (app_: FirebaseApp, _afAuth: AngularFireAuth) => {
- app = app_;
- afAuth = _afAuth;
- })();
+
+ app = TestBed.inject(FirebaseApp);
+ afAuth = TestBed.inject(AngularFireAuth);
});
afterEach(() => {
diff --git a/src/auth/auth.ts b/src/auth/auth.ts
index aa5c52b91..93eb9cc28 100644
--- a/src/auth/auth.ts
+++ b/src/auth/auth.ts
@@ -1,10 +1,20 @@
import { Injectable, Inject, Optional, NgZone, PLATFORM_ID } from '@angular/core';
import { Observable, of, from } from 'rxjs';
-import { switchMap, map, observeOn, shareReplay } from 'rxjs/operators';
-import { FIREBASE_OPTIONS, FIREBASE_APP_NAME, FirebaseOptions, FirebaseAppConfig, ɵPromiseProxy, ɵlazySDKProxy, ɵfirebaseAppFactory, ɵAngularFireSchedulers, ɵkeepUnstableUntilFirstFactory } from '@angular/fire';
+import { switchMap, map, observeOn, shareReplay, first } from 'rxjs/operators';
+import {
+ FIREBASE_OPTIONS,
+ FIREBASE_APP_NAME,
+ FirebaseOptions,
+ FirebaseAppConfig,
+ ɵPromiseProxy,
+ ɵlazySDKProxy,
+ ɵfirebaseAppFactory,
+ ɵAngularFireSchedulers,
+ ɵkeepUnstableUntilFirstFactory
+} from '@angular/fire';
import { User, auth } from 'firebase/app';
-export interface AngularFireAuth extends ɵPromiseProxy {};
+export interface AngularFireAuth extends ɵPromiseProxy {}
@Injectable({
providedIn: 'any'
@@ -34,31 +44,37 @@ export class AngularFireAuth {
public readonly idTokenResult: Observable;
constructor(
- @Inject(FIREBASE_OPTIONS) options:FirebaseOptions,
- @Optional() @Inject(FIREBASE_APP_NAME) nameOrConfig:string|FirebaseAppConfig|null|undefined,
+ @Inject(FIREBASE_OPTIONS) options: FirebaseOptions,
+ @Optional() @Inject(FIREBASE_APP_NAME) nameOrConfig: string|FirebaseAppConfig|null|undefined,
+ // tslint:disable-next-line:ban-types
@Inject(PLATFORM_ID) platformId: Object,
zone: NgZone
) {
const schedulers = new ɵAngularFireSchedulers(zone);
- const keepUnstableUntilFirst = ɵkeepUnstableUntilFirstFactory(schedulers, platformId);
+ const keepUnstableUntilFirst = ɵkeepUnstableUntilFirstFactory(schedulers);
const auth = of(undefined).pipe(
observeOn(schedulers.outsideAngular),
switchMap(() => zone.runOutsideAngular(() => import('firebase/auth'))),
map(() => ɵfirebaseAppFactory(options, zone, nameOrConfig)),
- map(app => app.auth()),
+ map(app => zone.runOutsideAngular(() => app.auth())),
shareReplay({ bufferSize: 1, refCount: false }),
);
+ // HACK, as we're exporting auth.Auth, rather than auth, developers importing firebase.auth
+ // (e.g, `import { auth } from 'firebase/app'`) are getting an undefined auth object unexpectedly
+ // as we're completely lazy. Let's eagerly load the Auth SDK here.
+ // There could potentially be race conditions still... but this greatly decreases the odds while
+ // we reevaluate the API.
+ const _ = auth.pipe(first()).subscribe();
+
this.authState = auth.pipe(
- observeOn(schedulers.outsideAngular),
- switchMap(auth => new Observable(auth.onAuthStateChanged.bind(auth))),
+ switchMap(auth => zone.runOutsideAngular(() => new Observable(auth.onAuthStateChanged.bind(auth)))),
keepUnstableUntilFirst
);
this.user = auth.pipe(
- observeOn(schedulers.outsideAngular),
- switchMap(auth => new Observable(auth.onIdTokenChanged.bind(auth))),
+ switchMap(auth => zone.runOutsideAngular(() => new Observable(auth.onIdTokenChanged.bind(auth)))),
keepUnstableUntilFirst
);
diff --git a/src/core/angularfire2.spec.ts b/src/core/angularfire2.spec.ts
index f90910a25..d62d0b481 100644
--- a/src/core/angularfire2.spec.ts
+++ b/src/core/angularfire2.spec.ts
@@ -1,24 +1,22 @@
-import { TestBed, inject } from '@angular/core/testing';
-import { PlatformRef, NgModule, CompilerFactory, NgZone } from '@angular/core';
-import { FirebaseApp, AngularFireModule } from './public_api';
-import { Subscription, Observable, Subject, of } from 'rxjs';
+import { TestBed } from '@angular/core/testing';
+import { CompilerFactory, NgModule, NgZone, PlatformRef } from '@angular/core';
+import { AngularFireModule, FirebaseApp } from './public_api';
+import { Observable, of, Subject } from 'rxjs';
import { COMMON_CONFIG } from '../test-config';
import { BrowserModule } from '@angular/platform-browser';
import { database } from 'firebase/app';
-import { ɵZoneScheduler, ɵkeepUnstableUntilFirstFactory, ɵAngularFireSchedulers } from './angularfire2';
-import { ɵPLATFORM_BROWSER_ID, ɵPLATFORM_SERVER_ID } from '@angular/common';
-import { tap } from 'rxjs/operators';
+import { ɵAngularFireSchedulers, ɵkeepUnstableUntilFirstFactory, ɵZoneScheduler } from './angularfire2';
+import { tap } from 'rxjs/operators';
import { TestScheduler } from 'rxjs/testing';
import { rando } from '../firestore/utils.spec';
describe('angularfire', () => {
- let subscription:Subscription;
let app: FirebaseApp;
let rootRef: database.Reference;
let questionsRef: database.Reference;
let listOfQuestionsRef: database.Reference;
let defaultPlatform: PlatformRef;
- let appName:string;
+ let appName: string;
beforeEach(() => {
@@ -28,27 +26,21 @@ describe('angularfire', () => {
imports: [AngularFireModule.initializeApp(COMMON_CONFIG, appName)]
});
- inject([FirebaseApp, PlatformRef], (_app: FirebaseApp, _platform: PlatformRef) => {
- app = _app;
- rootRef = app.database!().ref();
- questionsRef = rootRef.child('questions');
- listOfQuestionsRef = rootRef.child('list-of-questions');
- defaultPlatform = _platform;
- })();
-
+ app = TestBed.inject(FirebaseApp);
+ defaultPlatform = TestBed.inject(PlatformRef);
+ rootRef = app.database().ref();
+ questionsRef = rootRef.child('questions');
+ listOfQuestionsRef = rootRef.child('list-of-questions');
});
afterEach(() => {
- rootRef.remove()
- if(subscription && !subscription.closed) {
- subscription.unsubscribe();
- }
+ rootRef.remove();
app.delete();
});
describe('ZoneScheduler', () => {
it('should execute the scheduled work inside the specified zone', done => {
- let ngZone = Zone.current.fork({
+ const ngZone = Zone.current.fork({
name: 'ngZone'
});
const rootZone = Zone.current;
@@ -64,11 +56,11 @@ describe('angularfire', () => {
});
it('should execute nested scheduled work inside the specified zone', done => {
- const testScheduler = new TestScheduler(null!);
+ const testScheduler = new TestScheduler(null);
testScheduler.run(helpers => {
const outsideAngularScheduler = new ɵZoneScheduler(Zone.current, testScheduler);
- let ngZone = Zone.current.fork({
+ const ngZone = Zone.current.fork({
name: 'ngZone'
});
@@ -97,13 +89,13 @@ describe('angularfire', () => {
helpers.flush();
done();
expect(callbacksRan).toEqual(3);
- })
+ });
});
helpers.flush();
});
});
- })
- })
+ });
+ });
describe('keepUnstableUntilFirstFactory', () => {
let schedulers: ɵAngularFireSchedulers;
@@ -121,10 +113,10 @@ describe('angularfire', () => {
runTask: insideZone.run.bind(insideZone)
} as NgZone;
schedulers = new ɵAngularFireSchedulers(ngZone);
- })
+ });
it('should re-schedule emissions asynchronously', done => {
- const keepUnstableOp = ɵkeepUnstableUntilFirstFactory(schedulers, ɵPLATFORM_SERVER_ID);
+ const keepUnstableOp = ɵkeepUnstableUntilFirstFactory(schedulers);
let ran = false;
of(null).pipe(
@@ -133,38 +125,39 @@ describe('angularfire', () => {
).subscribe(() => {
expect(ran).toEqual(true);
done();
- }, () => fail("Should not error"));
+ }, () => fail('Should not error'));
expect(ran).toEqual(false);
});
- [ɵPLATFORM_SERVER_ID, ɵPLATFORM_BROWSER_ID].map(platformId =>
- it(`should subscribe outside angular and observe inside angular (${platformId})`, done => {
- const keepUnstableOp = ɵkeepUnstableUntilFirstFactory(schedulers, platformId);
-
- insideZone.run(() => {
- new Observable(s => {
- expect(Zone.current).toEqual(outsideZone);
- s.next("test");
- }).pipe(
- keepUnstableOp,
- tap(() => {
- expect(Zone.current).toEqual(insideZone);
- })
- ).subscribe(() => {
+ it(`should subscribe outside angular and observe inside angular`, done => {
+
+ const keepUnstableOp = ɵkeepUnstableUntilFirstFactory(schedulers);
+
+ insideZone.run(() => {
+ new Observable(s => {
+ expect(Zone.current).toEqual(outsideZone);
+ s.next('test');
+ }).pipe(
+ keepUnstableOp,
+ tap(() => {
expect(Zone.current).toEqual(insideZone);
- done();
- }, err => {
- fail(err);
- });
+ })
+ ).subscribe(() => {
+ expect(Zone.current).toEqual(insideZone);
+ done();
+ }, err => {
+ fail(err);
});
- })
- );
+ });
+
+ });
it('should block until first emission', done => {
- const testScheduler = new TestScheduler(null!);
+ const testScheduler = new TestScheduler(null);
testScheduler.run(helpers => {
const outsideZone = Zone.current;
+ // tslint:disable-next-line:no-string-literal
const taskTrack = new Zone['TaskTrackingZoneSpec']();
const insideZone = Zone.current.fork(taskTrack);
const trackingSchedulers: ɵAngularFireSchedulers = {
@@ -175,14 +168,18 @@ describe('angularfire', () => {
runTask: insideZone.run.bind(insideZone)
} as NgZone,
outsideAngular: new ɵZoneScheduler(outsideZone, testScheduler),
- insideAngular: new ɵZoneScheduler(insideZone, testScheduler),
+ insideAngular: new ɵZoneScheduler(insideZone, testScheduler)
};
- const keepUnstableOp = ɵkeepUnstableUntilFirstFactory(trackingSchedulers, ɵPLATFORM_SERVER_ID);
+ const keepUnstableOp = ɵkeepUnstableUntilFirstFactory(trackingSchedulers);
const s = new Subject();
s.pipe(
- keepUnstableOp,
- ).subscribe(() => { }, err => { fail(err); }, () => { });
+ keepUnstableOp
+ ).subscribe(() => {
+ }, err => {
+ fail(err);
+ }, () => {
+ });
// Flush to ensure all async scheduled functions are run
helpers.flush();
@@ -202,9 +199,9 @@ describe('angularfire', () => {
}, 150);
});
- })
+ });
- })
+ });
describe('FirebaseApp', () => {
@@ -223,13 +220,15 @@ describe('angularfire', () => {
imports: [
AngularFireModule.initializeApp(COMMON_CONFIG, appName),
BrowserModule
- ]})
+ ]
+ })
class MyModule {
- ngDoBootstrap() {}
+ ngDoBootstrap() {
+ }
}
const compilerFactory: CompilerFactory =
- defaultPlatform.injector.get(CompilerFactory, null);
+ defaultPlatform.injector.get(CompilerFactory, null);
const moduleFactory = compilerFactory.createCompiler().compileModuleSync(MyModule);
defaultPlatform.bootstrapModuleFactory(moduleFactory)
@@ -237,10 +236,10 @@ describe('angularfire', () => {
const ref = moduleRef.injector.get(FirebaseApp);
expect(ref.name).toEqual(app.name);
}).then(done, e => {
- fail(e);
- done()
- });
- })
+ fail(e);
+ done();
+ });
+ });
}
});
diff --git a/src/core/angularfire2.ts b/src/core/angularfire2.ts
index a8fa36d67..34841d582 100644
--- a/src/core/angularfire2.ts
+++ b/src/core/angularfire2.ts
@@ -1,14 +1,27 @@
import { NgZone } from '@angular/core';
-import { Observable, Subscription, SchedulerLike, SchedulerAction, queueScheduler, Operator, Subscriber, TeardownLogic, asyncScheduler } from 'rxjs';
-import { subscribeOn, observeOn, tap } from 'rxjs/operators';
-
-function noop() { }
+import {
+ asyncScheduler,
+ Observable,
+ Operator,
+ queueScheduler,
+ SchedulerAction,
+ SchedulerLike,
+ Subscriber,
+ Subscription,
+ TeardownLogic
+} from 'rxjs';
+import { observeOn, subscribeOn, tap } from 'rxjs/operators';
+
+function noop() {
+}
/**
* Schedules tasks so that they are invoked inside the Zone that is passed in the constructor.
*/
+// tslint:disable-next-line:class-name
export class ɵZoneScheduler implements SchedulerLike {
- constructor(private zone: any, private delegate: any = queueScheduler) { }
+ constructor(private zone: any, private delegate: any = queueScheduler) {
+ }
now() {
return this.delegate.now();
@@ -18,36 +31,38 @@ export class ɵZoneScheduler implements SchedulerLike {
const targetZone = this.zone;
// Wrap the specified work function to make sure that if nested scheduling takes place the
// work is executed in the correct zone
- const workInZone = function (this: SchedulerAction, state: any) {
+ const workInZone = function(this: SchedulerAction, state: any) {
targetZone.runGuarded(() => {
work.apply(this, [state]);
});
- }
+ };
// Scheduling itself needs to be run in zone to ensure setInterval calls for async scheduling are done
// inside the correct zone. This scheduler needs to schedule asynchronously always to ensure that
// firebase emissions are never synchronous. Specifying a delay causes issues with the queueScheduler delegate.
- return this.delegate.schedule(workInZone, delay, state)
+ return this.delegate.schedule(workInZone, delay, state);
}
}
+// tslint:disable-next-line:class-name
export class ɵBlockUntilFirstOperator implements Operator {
private task: MacroTask | null = null;
- constructor(private zone: any) { }
+ constructor(private zone: any) {
+ }
call(subscriber: Subscriber, source: Observable): TeardownLogic {
const unscheduleTask = this.unscheduleTask.bind(this);
this.task = this.zone.run(() => Zone.current.scheduleMacroTask('firebaseZoneBlock', noop, {}, noop, noop));
return source.pipe(
- tap(unscheduleTask, unscheduleTask, unscheduleTask)
+ tap({ next: unscheduleTask, complete: unscheduleTask, error: unscheduleTask })
).subscribe(subscriber).add(unscheduleTask);
}
private unscheduleTask() {
// maybe this is a race condition, invoke in a timeout
- // hold for 10ms while I try to figure out what is going on
+ // hold for 10ms while I try to figure out what is going on
setTimeout(() => {
if (this.task != null && this.task.state === 'scheduled') {
this.task.invoke();
@@ -57,6 +72,7 @@ export class ɵBlockUntilFirstOperator implements Operator {
}
}
+// tslint:disable-next-line:class-name
export class ɵAngularFireSchedulers {
public readonly outsideAngular: ɵZoneScheduler;
public readonly insideAngular: ɵZoneScheduler;
@@ -73,10 +89,7 @@ export class ɵAngularFireSchedulers {
* value from firebase but doesn't block the zone forever since the firebase subscription
* is still alive.
*/
-export function ɵkeepUnstableUntilFirstFactory(
- schedulers: ɵAngularFireSchedulers,
- platformId: Object
-) {
+export function ɵkeepUnstableUntilFirstFactory(schedulers: ɵAngularFireSchedulers) {
return function keepUnstableUntilFirst(obs$: Observable): Observable {
obs$ = obs$.lift(
new ɵBlockUntilFirstOperator(schedulers.ngZone)
@@ -90,17 +103,23 @@ export function ɵkeepUnstableUntilFirstFactory(
// INVESTIGATE https://github.com/angular/angularfire/pull/2315
// share()
);
- }
+ };
}
+// tslint:disable:ban-types
type FunctionPropertyNames = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T];
-type PromiseReturningFunctionPropertyNames = { [K in FunctionPropertyNames]: ReturnType extends Promise ? K : never }[FunctionPropertyNames];
-type NonPromiseReturningFunctionPropertyNames = { [K in FunctionPropertyNames]: ReturnType extends Promise ? never : K }[FunctionPropertyNames];
+type PromiseReturningFunctionPropertyNames = {
+ [K in FunctionPropertyNames]: ReturnType extends Promise ? K : never
+}[FunctionPropertyNames];
+type NonPromiseReturningFunctionPropertyNames = {
+ [K in FunctionPropertyNames]: ReturnType extends Promise ? never : K
+}[FunctionPropertyNames];
type NonFunctionPropertyNames = { [K in keyof T]: T[K] extends Function ? never : K }[keyof T];
+// tslint:enable:ban-types
export type ɵPromiseProxy = { [K in NonFunctionPropertyNames]: Promise } &
{ [K in NonPromiseReturningFunctionPropertyNames]: (...args: Parameters) => Promise> } &
- { [K in PromiseReturningFunctionPropertyNames ]: (...args: Parameters) => ReturnType };
+ { [K in PromiseReturningFunctionPropertyNames]: (...args: Parameters) => ReturnType };
// DEBUG quick debugger function for inline logging that typescript doesn't complain about
@@ -117,16 +136,21 @@ const noopFunctions = ['ngOnDestroy'];
// right now it's fairly simple but I'm sure this will grow in complexity
export const ɵlazySDKProxy = (klass: any, observable: Observable, zone: NgZone) => {
return new Proxy(klass, {
- get: (_, name:string) => zone.runOutsideAngular(() => {
- if (klass[name]) { return klass[name] }
- if (noopFunctions.includes(name)) { return () => {} }
- let promise = observable.toPromise().then(mod => {
+ get: (_, name: string) => zone.runOutsideAngular(() => {
+ if (klass[name]) {
+ return klass[name];
+ }
+ if (noopFunctions.includes(name)) {
+ return () => {
+ };
+ }
+ const promise = observable.toPromise().then(mod => {
const ret = mod && mod[name];
// TODO move to proper type guards
- if (typeof ret == 'function') {
+ if (typeof ret === 'function') {
return ret.bind(mod);
} else if (ret && ret.then) {
- return ret.then((res:any) => zone.run(() => res));
+ return ret.then((res: any) => zone.run(() => res));
} else {
return zone.run(() => ret);
}
@@ -134,10 +158,10 @@ export const ɵlazySDKProxy = (klass: any, observable: Observable, zone: Ng
// recurse the proxy
return new Proxy(() => undefined, {
get: (_, name) => promise[name],
- // TODO handle callbacks as transparently as I can
+ // TODO handle callbacks as transparently as I can
apply: (self, _, args) => promise.then(it => it && it(...args))
}
- )
+ );
})
- })
-};
\ No newline at end of file
+ });
+};
diff --git a/src/core/firebase.app.module.ts b/src/core/firebase.app.module.ts
index e3b8311e5..dc39f1894 100644
--- a/src/core/firebase.app.module.ts
+++ b/src/core/firebase.app.module.ts
@@ -1,69 +1,76 @@
-import { InjectionToken, NgModule, Optional, NgZone, VERSION as NG_VERSION, Version, PLATFORM_ID, Inject } from '@angular/core';
-import { app, auth, database, messaging, storage, firestore, functions, analytics, performance, remoteConfig } from 'firebase/app';
+import { Inject, InjectionToken, NgModule, NgZone, Optional, PLATFORM_ID, VERSION as NG_VERSION, Version } from '@angular/core';
import * as firebase from 'firebase/app';
+import { analytics, app, auth, database, firestore, functions, messaging, performance, remoteConfig, storage } from 'firebase/app';
// INVESTIGATE Public types don't expose FirebaseOptions or FirebaseAppConfig, is this the case anylonger?
-export interface FirebaseOptions {[key:string]: any};
-export interface FirebaseAppConfig {[key:string]: any};
+export interface FirebaseOptions {
+ [key: string]: any;
+}
+
+export interface FirebaseAppConfig {
+ [key: string]: any;
+}
export const FIREBASE_OPTIONS = new InjectionToken('angularfire2.app.options');
-export const FIREBASE_APP_NAME = new InjectionToken('angularfire2.app.nameOrConfig');
+export const FIREBASE_APP_NAME = new InjectionToken('angularfire2.app.nameOrConfig');
// Have to implement as we need to return a class from the provider, we should consider exporting
// this in the firebase/app types as this is our highest risk of breaks
export class FirebaseApp implements Partial {
- name: string;
- options: {};
- analytics: () => analytics.Analytics;
- auth: () => auth.Auth;
- database: (databaseURL?: string) => database.Database;
- messaging: () => messaging.Messaging;
- performance: () => performance.Performance;
- storage: (storageBucket?: string) => storage.Storage;
- delete: () => Promise;
- firestore: () => firestore.Firestore;
- functions: (region?: string) => functions.Functions;
- remoteConfig: () => remoteConfig.RemoteConfig;
+ name: string;
+ options: {};
+ analytics: () => analytics.Analytics;
+ auth: () => auth.Auth;
+ database: (databaseURL?: string) => database.Database;
+ messaging: () => messaging.Messaging;
+ performance: () => performance.Performance;
+ storage: (storageBucket?: string) => storage.Storage;
+ delete: () => Promise;
+ firestore: () => firestore.Firestore;
+ functions: (region?: string) => functions.Functions;
+ remoteConfig: () => remoteConfig.RemoteConfig;
}
export const VERSION = new Version('ANGULARFIRE2_VERSION');
-export function ɵfirebaseAppFactory(options: FirebaseOptions, zone: NgZone, nameOrConfig?: string|FirebaseAppConfig|null) {
- const name = typeof nameOrConfig === 'string' && nameOrConfig || '[DEFAULT]';
- const config = typeof nameOrConfig === 'object' && nameOrConfig || {};
- config.name = config.name || name;
- // Added any due to some inconsistency between @firebase/app and firebase types
- const existingApp = firebase.apps.filter(app => app && app.name === config.name)[0] as any;
- // We support FirebaseConfig, initializeApp's public type only accepts string; need to cast as any
- // Could be solved with https://github.com/firebase/firebase-js-sdk/pull/1206
- return (existingApp || zone.runOutsideAngular(() => firebase.initializeApp(options, config as any))) as FirebaseApp;
+export function ɵfirebaseAppFactory(options: FirebaseOptions, zone: NgZone, nameOrConfig?: string | FirebaseAppConfig | null) {
+ const name = typeof nameOrConfig === 'string' && nameOrConfig || '[DEFAULT]';
+ const config = typeof nameOrConfig === 'object' && nameOrConfig || {};
+ config.name = config.name || name;
+ // Added any due to some inconsistency between @firebase/app and firebase types
+ const existingApp = firebase.apps.filter(app => app && app.name === config.name)[0] as any;
+ // We support FirebaseConfig, initializeApp's public type only accepts string; need to cast as any
+ // Could be solved with https://github.com/firebase/firebase-js-sdk/pull/1206
+ return (existingApp || zone.runOutsideAngular(() => firebase.initializeApp(options, config as any))) as FirebaseApp;
}
-const FirebaseAppProvider = {
- provide: FirebaseApp,
- useFactory: ɵfirebaseAppFactory,
- deps: [
- FIREBASE_OPTIONS,
- NgZone,
- [new Optional(), FIREBASE_APP_NAME]
- ]
+const FIREBASE_APP_PROVIDER = {
+ provide: FirebaseApp,
+ useFactory: ɵfirebaseAppFactory,
+ deps: [
+ FIREBASE_OPTIONS,
+ NgZone,
+ [new Optional(), FIREBASE_APP_NAME]
+ ]
};
-
+
@NgModule({
- providers: [ FirebaseAppProvider ],
+ providers: [FIREBASE_APP_PROVIDER]
})
export class AngularFireModule {
- static initializeApp(options: FirebaseOptions, nameOrConfig?: string | FirebaseAppConfig) {
- return {
- ngModule: AngularFireModule,
- providers: [
- { provide: FIREBASE_OPTIONS, useValue: options },
- { provide: FIREBASE_APP_NAME, useValue: nameOrConfig }
- ]
- }
- }
- constructor(@Inject(PLATFORM_ID) platformId:Object ) {
- firebase.registerVersion('angularfire', VERSION.full, platformId.toString());
- firebase.registerVersion('angular', NG_VERSION.full);
- }
-}
\ No newline at end of file
+ static initializeApp(options: FirebaseOptions, nameOrConfig?: string | FirebaseAppConfig) {
+ return {
+ ngModule: AngularFireModule,
+ providers: [
+ { provide: FIREBASE_OPTIONS, useValue: options },
+ { provide: FIREBASE_APP_NAME, useValue: nameOrConfig }
+ ]
+ };
+ }
+
+ // tslint:disable-next-line:ban-types
+ constructor(@Inject(PLATFORM_ID) platformId: Object) {
+ firebase.registerVersion('angularfire', VERSION.full, platformId.toString());
+ firebase.registerVersion('angular', NG_VERSION.full);
+ }
+}
diff --git a/src/core/public_api.ts b/src/core/public_api.ts
index edf1b3425..77f50e30e 100644
--- a/src/core/public_api.ts
+++ b/src/core/public_api.ts
@@ -1,2 +1,2 @@
export * from './angularfire2';
-export * from './firebase.app.module';
\ No newline at end of file
+export * from './firebase.app.module';
diff --git a/src/database/database.spec.ts b/src/database/database.spec.ts
index 3834d43d1..fce81c16c 100644
--- a/src/database/database.spec.ts
+++ b/src/database/database.spec.ts
@@ -1,6 +1,6 @@
-import { FirebaseApp, AngularFireModule, FIREBASE_OPTIONS, FIREBASE_APP_NAME } from '@angular/fire';
+import { AngularFireModule, FIREBASE_APP_NAME, FIREBASE_OPTIONS, FirebaseApp } from '@angular/fire';
import { AngularFireDatabase, AngularFireDatabaseModule, URL } from './public_api';
-import { TestBed, inject } from '@angular/core/testing';
+import { TestBed } from '@angular/core/testing';
import { COMMON_CONFIG } from '../test-config';
import { NgZone } from '@angular/core';
import 'firebase/database';
@@ -9,7 +9,7 @@ import { rando } from '../firestore/utils.spec';
describe('AngularFireDatabase', () => {
let app: FirebaseApp;
let db: AngularFireDatabase;
- let zone: NgZone
+ let zone: NgZone;
let firebaseAppName: string;
beforeEach(() => {
@@ -20,14 +20,13 @@ describe('AngularFireDatabase', () => {
AngularFireDatabaseModule
],
providers: [
- { provide: URL, useValue: 'http://localhost:9000'}
+ { provide: URL, useValue: 'http://localhost:9000' }
]
});
- inject([FirebaseApp, AngularFireDatabase, NgZone], (app_: FirebaseApp, _db: AngularFireDatabase, _zone: NgZone) => {
- app = app_;
- db = _db;
- zone = _zone;
- })();
+
+ app = TestBed.inject(FirebaseApp);
+ db = TestBed.inject(AngularFireDatabase);
+ zone = TestBed.inject(NgZone);
});
afterEach(() => {
@@ -45,9 +44,9 @@ describe('AngularFireDatabase', () => {
});
it('should accept a Firebase App in the constructor', (done) => {
- const __db = new AngularFireDatabase(app.options, rando(), undefined!, {}, zone);
- expect(__db instanceof AngularFireDatabase).toEqual(true);
- __db.database.app.delete().then(done, done);
+ const database = new AngularFireDatabase(app.options, rando(), undefined, {}, zone);
+ expect(database instanceof AngularFireDatabase).toEqual(true);
+ database.database.app.delete().then(done, done);
});
it('should have an initialized Firebase app instance member', () => {
@@ -68,7 +67,7 @@ describe('AngularFireDatabase w/options', () => {
beforeEach(() => {
query = rando();
firebaseAppName = rando();
- url = `http://localhost:${Math.floor(Math.random()*9999)}`;
+ url = `http://localhost:${Math.floor(Math.random() * 9999)}`;
TestBed.configureTestingModule({
imports: [
AngularFireModule.initializeApp(COMMON_CONFIG, rando()),
@@ -80,10 +79,9 @@ describe('AngularFireDatabase w/options', () => {
{ provide: URL, useValue: url }
]
});
- inject([FirebaseApp, AngularFireDatabase], (app_: FirebaseApp, _db: AngularFireDatabase) => {
- app = app_;
- db = _db;
- })();
+
+ app = TestBed.inject(FirebaseApp);
+ db = TestBed.inject(AngularFireDatabase);
});
afterEach(() => {
@@ -104,20 +102,20 @@ describe('AngularFireDatabase w/options', () => {
expect(db.database.app.name).toEqual(firebaseAppName);
});
-/* INVESTIGATE database(url) does not seem to be working
+ /* INVESTIGATE database(url) does not seem to be working
- it('database be pointing to the provided DB instance', () => {
- expect(db.database.ref().toString()).toEqual(url);
- });
+ it('database be pointing to the provided DB instance', () => {
+ expect(db.database.ref().toString()).toEqual(url);
+ });
- it('list should be using the provided DB instance', () => {
- expect(db.list(query).query.toString()).toEqual(`${url}/${query}`);
- });
+ it('list should be using the provided DB instance', () => {
+ expect(db.list(query).query.toString()).toEqual(`${url}/${query}`);
+ });
- it('object should be using the provided DB instance', () => {
- expect(db.object(query).query.toString()).toEqual(`${url}/${query}`);
- });
-*/
+ it('object should be using the provided DB instance', () => {
+ expect(db.object(query).query.toString()).toEqual(`${url}/${query}`);
+ });
+ */
});
});
diff --git a/src/database/database.ts b/src/database/database.ts
index 9f2c086bb..6cd94d6a4 100644
--- a/src/database/database.ts
+++ b/src/database/database.ts
@@ -1,14 +1,22 @@
-import { Injectable, Inject, Optional, NgZone, PLATFORM_ID, InjectionToken } from '@angular/core';
-import { DatabaseQuery, PathReference, QueryFn, AngularFireList, AngularFireObject } from './interfaces';
+import { Inject, Injectable, InjectionToken, NgZone, Optional, PLATFORM_ID } from '@angular/core';
+import { AngularFireList, AngularFireObject, DatabaseQuery, PathReference, QueryFn } from './interfaces';
import { getRef } from './utils';
import { createListReference } from './list/create-reference';
import { createObjectReference } from './object/create-reference';
-import { FirebaseOptions, FirebaseAppConfig, FIREBASE_OPTIONS, FIREBASE_APP_NAME, ɵfirebaseAppFactory, ɵkeepUnstableUntilFirstFactory, ɵAngularFireSchedulers } from '@angular/fire';
+import {
+ FIREBASE_APP_NAME,
+ FIREBASE_OPTIONS,
+ FirebaseAppConfig,
+ FirebaseOptions,
+ ɵAngularFireSchedulers,
+ ɵfirebaseAppFactory,
+ ɵkeepUnstableUntilFirstFactory
+} from '@angular/fire';
import { Observable } from 'rxjs';
import { database } from 'firebase/app';
import 'firebase/database';
-export const URL = new InjectionToken('angularfire2.realtimeDatabaseURL')
+export const URL = new InjectionToken('angularfire2.realtimeDatabaseURL');
@Injectable({
providedIn: 'any'
@@ -20,14 +28,15 @@ export class AngularFireDatabase {
public readonly keepUnstableUntilFirst: (obs$: Observable) => Observable;
constructor(
- @Inject(FIREBASE_OPTIONS) options:FirebaseOptions,
- @Optional() @Inject(FIREBASE_APP_NAME) nameOrConfig:string|FirebaseAppConfig|null|undefined,
- @Optional() @Inject(URL) databaseURL:string|null,
+ @Inject(FIREBASE_OPTIONS) options: FirebaseOptions,
+ @Optional() @Inject(FIREBASE_APP_NAME) nameOrConfig: string | FirebaseAppConfig | null | undefined,
+ @Optional() @Inject(URL) databaseURL: string | null,
+ // tslint:disable-next-line:ban-types
@Inject(PLATFORM_ID) platformId: Object,
zone: NgZone
) {
this.schedulers = new ɵAngularFireSchedulers(zone);
- this.keepUnstableUntilFirst = ɵkeepUnstableUntilFirstFactory(this.schedulers, platformId);
+ this.keepUnstableUntilFirst = ɵkeepUnstableUntilFirstFactory(this.schedulers);
this.database = zone.runOutsideAngular(() => {
const app = ɵfirebaseAppFactory(options, zone, nameOrConfig);
@@ -38,13 +47,13 @@ export class AngularFireDatabase {
list(pathOrRef: PathReference, queryFn?: QueryFn): AngularFireList {
const ref = getRef(this.database, pathOrRef);
let query: DatabaseQuery = ref;
- if(queryFn) {
+ if (queryFn) {
query = queryFn(ref);
}
return createListReference(query, this);
}
- object(pathOrRef: PathReference): AngularFireObject {
+ object(pathOrRef: PathReference): AngularFireObject {
const ref = getRef(this.database, pathOrRef);
return createObjectReference(ref, this);
}
diff --git a/src/database/interfaces.ts b/src/database/interfaces.ts
index d2ce4e8f3..e1ff15b3a 100644
--- a/src/database/interfaces.ts
+++ b/src/database/interfaces.ts
@@ -38,7 +38,7 @@ export type ListenEvent = 'value' | ChildEvent;
export interface Action {
type: ListenEvent;
payload: T;
-};
+}
export interface AngularFireAction extends Action {
prevKey: string | null | undefined;
diff --git a/src/database/list/audit-trail.spec.ts b/src/database/list/audit-trail.spec.ts
index baa817e7f..c866417a5 100644
--- a/src/database/list/audit-trail.spec.ts
+++ b/src/database/list/audit-trail.spec.ts
@@ -1,7 +1,7 @@
import { DatabaseReference } from '../interfaces';
-import { FirebaseApp, AngularFireModule } from '@angular/fire';
+import { AngularFireModule, FirebaseApp } from '@angular/fire';
import { AngularFireDatabase, AngularFireDatabaseModule, auditTrail, ChildEvent, URL } from '../public_api';
-import { TestBed, inject } from '@angular/core/testing';
+import { TestBed } from '@angular/core/testing';
import { COMMON_CONFIG } from '../../test-config';
import { skip } from 'rxjs/operators';
import 'firebase/database';
@@ -12,10 +12,9 @@ describe('auditTrail', () => {
let db: AngularFireDatabase;
let createRef: (path: string) => DatabaseReference;
let batch = {};
- const items = [{ name: 'zero' }, { name: 'one' }, { name: 'two' }].map((item, i) => ( { key: i.toString(), ...item } ));
- Object.keys(items).forEach(function (key, i) {
- const itemValue = items[key];
- batch[i] = itemValue;
+ const items = [{ name: 'zero' }, { name: 'one' }, { name: 'two' }].map((item, i) => ({ key: i.toString(), ...item }));
+ Object.keys(items).forEach((key, i) => {
+ batch[i] = items[key];
});
// make batch immutable to preserve integrity
batch = Object.freeze(batch);
@@ -30,11 +29,10 @@ describe('auditTrail', () => {
{ provide: URL, useValue: 'http://localhost:9000' }
]
});
- inject([FirebaseApp, AngularFireDatabase], (app_: FirebaseApp, _db: AngularFireDatabase) => {
- app = app_;
- db = _db;
- createRef = (path: string) => db.database.ref(path);
- })();
+
+ app = TestBed.inject(FirebaseApp);
+ db = TestBed.inject(AngularFireDatabase);
+ createRef = (path: string) => db.database.ref(path);
});
afterEach(() => {
diff --git a/src/database/list/audit-trail.ts b/src/database/list/audit-trail.ts
index 6461dec72..1ead7342b 100644
--- a/src/database/list/audit-trail.ts
+++ b/src/database/list/audit-trail.ts
@@ -1,9 +1,9 @@
-import { DataSnapshot, DatabaseQuery, ChildEvent, DatabaseSnapshot, AngularFireAction, SnapshotAction } from '../interfaces';
+import { AngularFireAction, ChildEvent, DatabaseQuery, DataSnapshot, SnapshotAction } from '../interfaces';
import { stateChanges } from './state-changes';
import { Observable, SchedulerLike } from 'rxjs';
import { fromRef } from '../observable/fromRef';
-import { skipWhile, withLatestFrom, map, scan } from 'rxjs/operators';
+import { map, scan, skipWhile, withLatestFrom } from 'rxjs/operators';
export function auditTrail(query: DatabaseQuery, events?: ChildEvent[], scheduler?: SchedulerLike): Observable[]> {
const auditTrail$ = stateChanges(query, events)
@@ -46,10 +46,10 @@ function waitForLoaded(query: DatabaseQuery, action$: Observable {
// Store the last key in the data set
- let lastKeyToLoad = loaded.lastKeyToLoad;
+ const lastKeyToLoad = loaded.lastKeyToLoad;
// Store all child keys loaded at this point
const loadedKeys = actions.map(snap => snap.key);
- return { actions, lastKeyToLoad, loadedKeys }
+ return { actions, lastKeyToLoad, loadedKeys };
}),
// This is the magical part, only emit when the last load key
// in the dataset has been loaded by a child event. At this point
diff --git a/src/database/list/changes.spec.ts b/src/database/list/changes.spec.ts
index 75e70dd05..c21c2a8bb 100644
--- a/src/database/list/changes.spec.ts
+++ b/src/database/list/changes.spec.ts
@@ -1,9 +1,9 @@
import { database } from 'firebase/app';
-import { FirebaseApp, AngularFireModule } from '@angular/fire';
+import { AngularFireModule, FirebaseApp } from '@angular/fire';
import { AngularFireDatabase, AngularFireDatabaseModule, listChanges, URL } from '../public_api';
-import { TestBed, inject } from '@angular/core/testing';
+import { TestBed } from '@angular/core/testing';
import { COMMON_CONFIG } from '../../test-config';
-import { skip, take, tap } from 'rxjs/operators';
+import { skip, take } from 'rxjs/operators';
import 'firebase/database';
import { rando } from '../../firestore/utils.spec';
@@ -12,10 +12,9 @@ describe('listChanges', () => {
let db: AngularFireDatabase;
let ref: (path: string) => database.Reference;
let batch = {};
- const items = [{ name: 'zero' }, { name: 'one' }, { name: 'two' }].map((item, i) => ( { key: i.toString(), ...item } ));
- Object.keys(items).forEach(function (key, i) {
- const itemValue = items[key];
- batch[i] = itemValue;
+ const items = [{ name: 'zero' }, { name: 'one' }, { name: 'two' }].map((item, i) => ({ key: i.toString(), ...item }));
+ Object.keys(items).forEach((key, i) => {
+ batch[i] = items[key];
});
// make batch immutable to preserve integrity
batch = Object.freeze(batch);
@@ -30,11 +29,10 @@ describe('listChanges', () => {
{ provide: URL, useValue: 'http://localhost:9000' }
]
});
- inject([FirebaseApp, AngularFireDatabase], (app_: FirebaseApp, _db: AngularFireDatabase) => {
- app = app_;
- db = _db;
- ref = (path: string) => db.database.ref(path);
- })();
+
+ app = TestBed.inject(FirebaseApp);
+ db = TestBed.inject(AngularFireDatabase);
+ ref = (path: string) => db.database.ref(path);
});
afterEach(() => {
@@ -46,7 +44,7 @@ describe('listChanges', () => {
it('should stream value at first', (done) => {
const someRef = ref(rando());
const obs = listChanges(someRef, ['child_added']);
- const sub = obs.pipe(take(1)).subscribe(changes => {
+ obs.pipe(take(1)).subscribe(changes => {
const data = changes.map(change => change.payload.val());
expect(data).toEqual(items);
}).add(done);
@@ -56,7 +54,7 @@ describe('listChanges', () => {
it('should process a new child_added event', done => {
const aref = ref(rando());
const obs = listChanges(aref, ['child_added']);
- const sub = obs.pipe(skip(1),take(1)).subscribe(changes => {
+ obs.pipe(skip(1), take(1)).subscribe(changes => {
const data = changes.map(change => change.payload.val());
expect(data[3]).toEqual({ name: 'anotha one' });
}).add(done);
@@ -67,7 +65,7 @@ describe('listChanges', () => {
it('should stream in order events', (done) => {
const aref = ref(rando());
const obs = listChanges(aref.orderByChild('name'), ['child_added']);
- const sub = obs.pipe(take(1)).subscribe(changes => {
+ obs.pipe(take(1)).subscribe(changes => {
const names = changes.map(change => change.payload.val().name);
expect(names[0]).toEqual('one');
expect(names[1]).toEqual('two');
@@ -79,7 +77,7 @@ describe('listChanges', () => {
it('should stream in order events w/child_added', (done) => {
const aref = ref(rando());
const obs = listChanges(aref.orderByChild('name'), ['child_added']);
- const sub = obs.pipe(skip(1),take(1)).subscribe(changes => {
+ obs.pipe(skip(1), take(1)).subscribe(changes => {
const names = changes.map(change => change.payload.val().name);
expect(names[0]).toEqual('anotha one');
expect(names[1]).toEqual('one');
@@ -93,7 +91,7 @@ describe('listChanges', () => {
it('should stream events filtering', (done) => {
const aref = ref(rando());
const obs = listChanges(aref.orderByChild('name').equalTo('zero'), ['child_added']);
- obs.pipe(skip(1),take(1)).subscribe(changes => {
+ obs.pipe(skip(1), take(1)).subscribe(changes => {
const names = changes.map(change => change.payload.val().name);
expect(names[0]).toEqual('zero');
expect(names[1]).toEqual('zero');
diff --git a/src/database/list/changes.ts b/src/database/list/changes.ts
index f47b3cf3c..cb37d0694 100644
--- a/src/database/list/changes.ts
+++ b/src/database/list/changes.ts
@@ -1,17 +1,17 @@
import { fromRef } from '../observable/fromRef';
-import { Observable, of, merge, SchedulerLike } from 'rxjs';
+import { merge, Observable, of, SchedulerLike } from 'rxjs';
-import { DatabaseQuery, ChildEvent, SnapshotAction } from '../interfaces';
+import { ChildEvent, DatabaseQuery, SnapshotAction } from '../interfaces';
import { isNil } from '../utils';
-import { switchMap, distinctUntilChanged, scan } from 'rxjs/operators';
+import { distinctUntilChanged, scan, switchMap } from 'rxjs/operators';
-export function listChanges(ref: DatabaseQuery, events: ChildEvent[], scheduler?: SchedulerLike): Observable[]> {
+export function listChanges(ref: DatabaseQuery, events: ChildEvent[], scheduler?: SchedulerLike): Observable[]> {
return fromRef(ref, 'value', 'once', scheduler).pipe(
switchMap(snapshotAction => {
const childEvent$ = [of(snapshotAction)];
events.forEach(event => childEvent$.push(fromRef(ref, event, 'on', scheduler)));
- return merge(...childEvent$).pipe(scan(buildView, []))
+ return merge(...childEvent$).pipe(scan(buildView, []));
}),
distinctUntilChanged()
);
@@ -19,8 +19,8 @@ export function listChanges(ref: DatabaseQuery, events: ChildEvent[], sch
function positionFor(changes: SnapshotAction[], key) {
const len = changes.length;
- for(let i=0; i(changes: SnapshotAction[], key) {
}
function positionAfter(changes: SnapshotAction[], prevKey?: string) {
- if(isNil(prevKey)) {
+ if (isNil(prevKey)) {
return 0;
} else {
const i = positionFor(changes, prevKey);
- if( i === -1) {
+ if (i === -1) {
return changes.length;
} else {
return i + 1;
@@ -41,7 +41,7 @@ function positionAfter(changes: SnapshotAction[], prevKey?: string) {
}
function buildView(current, action) {
- const { payload, type, prevKey, key } = action;
+ const { payload, prevKey, key } = action;
const currentKeyPosition = positionFor(current, key);
const afterPreviousKeyPosition = positionAfter(current, prevKey);
switch (action.type) {
@@ -49,7 +49,7 @@ function buildView(current, action) {
if (action.payload && action.payload.exists()) {
let prevKey = null;
action.payload.forEach(payload => {
- const action = {payload, type: 'value', prevKey, key: payload.key};
+ const action = { payload, type: 'value', prevKey, key: payload.key };
prevKey = payload.key;
current = [...current, action];
return false;
@@ -60,14 +60,14 @@ function buildView(current, action) {
if (currentKeyPosition > -1) {
// check that the previouskey is what we expect, else reorder
const previous = current[currentKeyPosition - 1];
- if ((previous && previous.key || null) != prevKey) {
+ if ((previous && previous.key || null) !== prevKey) {
current = current.filter(x => x.payload.key !== payload.key);
current.splice(afterPreviousKeyPosition, 0, action);
}
} else if (prevKey == null) {
return [action, ...current];
} else {
- current = current.slice()
+ current = current.slice();
current.splice(afterPreviousKeyPosition, 0, action);
}
return current;
@@ -76,9 +76,9 @@ function buildView(current, action) {
case 'child_changed':
return current.map(x => x.payload.key === key ? action : x);
case 'child_moved':
- if(currentKeyPosition > -1) {
+ if (currentKeyPosition > -1) {
const data = current.splice(currentKeyPosition, 1)[0];
- current = current.slice()
+ current = current.slice();
current.splice(afterPreviousKeyPosition, 0, data);
return current;
}
diff --git a/src/database/list/create-reference.ts b/src/database/list/create-reference.ts
index 69359e22d..b80136a62 100644
--- a/src/database/list/create-reference.ts
+++ b/src/database/list/create-reference.ts
@@ -1,4 +1,4 @@
-import { DatabaseQuery, AngularFireList, ChildEvent } from '../interfaces';
+import { AngularFireList, ChildEvent, DatabaseQuery } from '../interfaces';
import { snapshotChanges } from './snapshot-changes';
import { stateChanges } from './state-changes';
import { auditTrail } from './audit-trail';
@@ -7,14 +7,15 @@ import { createRemoveMethod } from './remove';
import { AngularFireDatabase } from '../database';
import { map } from 'rxjs/operators';
-export function createListReference(query: DatabaseQuery, afDatabase: AngularFireDatabase): AngularFireList {
+export function createListReference(query: DatabaseQuery, afDatabase: AngularFireDatabase): AngularFireList {
const outsideAngularScheduler = afDatabase.schedulers.outsideAngular;
+ const refInZone = afDatabase.schedulers.ngZone.run(() => query.ref);
return {
query,
- update: createDataOperationMethod>(query.ref, 'update'),
- set: createDataOperationMethod(query.ref, 'set'),
- push: (data: T) => query.ref.push(data),
- remove: createRemoveMethod(query.ref),
+ update: createDataOperationMethod>(refInZone, 'update'),
+ set: createDataOperationMethod(refInZone, 'set'),
+ push: (data: T) => refInZone.push(data),
+ remove: createRemoveMethod(refInZone),
snapshotChanges(events?: ChildEvent[]) {
return snapshotChanges(query, events, outsideAngularScheduler).pipe(afDatabase.keepUnstableUntilFirst);
},
@@ -31,5 +32,5 @@ export function createListReference(query: DatabaseQuery, afDatabase: Ang
afDatabase.keepUnstableUntilFirst
);
}
- }
+ };
}
diff --git a/src/database/list/data-operation.ts b/src/database/list/data-operation.ts
index 0bc1a0210..78bb0bf4e 100644
--- a/src/database/list/data-operation.ts
+++ b/src/database/list/data-operation.ts
@@ -1,13 +1,12 @@
-
-import { DatabaseReference, FirebaseOperation, DatabaseSnapshot } from '../interfaces';
+import { DatabaseReference, DatabaseSnapshot, FirebaseOperation } from '../interfaces';
import { checkOperationCases } from '../utils';
export function createDataOperationMethod(ref: DatabaseReference, operation: string) {
return function dataOperation(item: FirebaseOperation, value: T) {
return checkOperationCases(item, {
- stringCase: () => ref.child(item)[operation](value),
- firebaseCase: () => (item)[operation](value),
- snapshotCase: () => (>item).ref[operation](value)
+ stringCase: () => ref.child(item as string)[operation](value),
+ firebaseCase: () => (item as DatabaseReference)[operation](value),
+ snapshotCase: () => (item as DatabaseSnapshot).ref[operation](value)
});
- }
+ };
}
diff --git a/src/database/list/remove.ts b/src/database/list/remove.ts
index 49751a340..45928df86 100644
--- a/src/database/list/remove.ts
+++ b/src/database/list/remove.ts
@@ -1,16 +1,15 @@
-import { DatabaseReference, DataSnapshot, FirebaseOperation, DatabaseSnapshot } from '../interfaces';
+import { DatabaseReference, DatabaseSnapshot, FirebaseOperation } from '../interfaces';
import { checkOperationCases } from '../utils';
-import { database } from 'firebase/app';
// TODO(davideast): Find out why TS thinks this returns firebase.Primise
// instead of Promise.
export function createRemoveMethod(ref: DatabaseReference) {
return function remove(item?: FirebaseOperation): any {
- if(!item) { return ref.remove(); }
+ if (!item) { return ref.remove(); }
return checkOperationCases(item, {
- stringCase: () => ref.child(item).remove(),
- firebaseCase: () => (item).remove(),
- snapshotCase: () => (>item).ref.remove()
+ stringCase: () => ref.child(item as string).remove(),
+ firebaseCase: () => (item as DatabaseReference).remove(),
+ snapshotCase: () => (item as DatabaseSnapshot).ref.remove()
});
- }
+ };
}
diff --git a/src/database/list/snapshot-changes.spec.ts b/src/database/list/snapshot-changes.spec.ts
index bfed665a5..6ba1959ea 100644
--- a/src/database/list/snapshot-changes.spec.ts
+++ b/src/database/list/snapshot-changes.spec.ts
@@ -1,10 +1,10 @@
import { database } from 'firebase/app';
-import { FirebaseApp, AngularFireModule } from '@angular/fire';
-import { AngularFireDatabase, AngularFireDatabaseModule, snapshotChanges, ChildEvent, URL } from '../public_api';
-import { TestBed, inject } from '@angular/core/testing';
+import { AngularFireModule, FirebaseApp } from '@angular/fire';
+import { AngularFireDatabase, AngularFireDatabaseModule, ChildEvent, snapshotChanges, URL } from '../public_api';
+import { TestBed } from '@angular/core/testing';
import { COMMON_CONFIG } from '../../test-config';
import { BehaviorSubject } from 'rxjs';
-import { skip, take, switchMap } from 'rxjs/operators';
+import { skip, switchMap, take } from 'rxjs/operators';
import 'firebase/database';
import { rando } from '../../firestore/utils.spec';
@@ -13,10 +13,9 @@ describe('snapshotChanges', () => {
let db: AngularFireDatabase;
let createRef: (path: string) => database.Reference;
let batch = {};
- const items = [{ name: 'zero' }, { name: 'one' }, { name: 'two' }].map((item, i) => ( { key: i.toString(), ...item } ));
- Object.keys(items).forEach(function (key, i) {
- const itemValue = items[key];
- batch[i] = itemValue;
+ const items = [{ name: 'zero' }, { name: 'one' }, { name: 'two' }].map((item, i) => ({ key: i.toString(), ...item }));
+ Object.keys(items).forEach((key, i) => {
+ batch[i] = items[key];
});
// make batch immutable to preserve integrity
batch = Object.freeze(batch);
@@ -31,11 +30,10 @@ describe('snapshotChanges', () => {
{ provide: URL, useValue: 'http://localhost:9000' }
]
});
- inject([FirebaseApp, AngularFireDatabase], (app_: FirebaseApp, _db: AngularFireDatabase) => {
- app = app_;
- db = _db;
- createRef = (path: string) => db.database.ref(path);;
- })();
+
+ app = TestBed.inject(FirebaseApp);
+ db = TestBed.inject(AngularFireDatabase);
+ createRef = (path: string) => db.database.ref(path);
});
afterEach(() => {
@@ -55,7 +53,7 @@ describe('snapshotChanges', () => {
it('should listen to all events by default', (done) => {
const { snapChanges, ref } = prepareSnapshotChanges();
snapChanges.pipe(take(1)).subscribe(actions => {
- const data = actions.map(a => a.payload!.val());
+ const data = actions.map(a => a.payload.val());
expect(data).toEqual(items);
}).add(done);
ref.set(batch);
@@ -63,9 +61,10 @@ describe('snapshotChanges', () => {
it('should handle multiple subscriptions (hot)', (done) => {
const { snapChanges, ref } = prepareSnapshotChanges();
- const sub = snapChanges.subscribe(() => {}).add(done);
+ const sub = snapChanges.subscribe(() => {
+ }).add(done);
snapChanges.pipe(take(1)).subscribe(actions => {
- const data = actions.map(a => a.payload!.val());
+ const data = actions.map(a => a.payload.val());
expect(data).toEqual(items);
}).add(sub);
ref.set(batch);
@@ -73,19 +72,20 @@ describe('snapshotChanges', () => {
it('should handle multiple subscriptions (warm)', done => {
const { snapChanges, ref } = prepareSnapshotChanges();
- snapChanges.pipe(take(1)).subscribe(() => {}).add(() => {
+ snapChanges.pipe(take(1)).subscribe(() => {
+ }).add(() => {
snapChanges.pipe(take(1)).subscribe(actions => {
- const data = actions.map(a => a.payload!.val());
+ const data = actions.map(a => a.payload.val());
expect(data).toEqual(items);
}).add(done);
});
ref.set(batch);
});
- it('should listen to only child_added events', (done) => {
+ it('should listen to only child_added events', (done) => {
const { snapChanges, ref } = prepareSnapshotChanges({ events: ['child_added'], skipnumber: 0 });
snapChanges.pipe(take(1)).subscribe(actions => {
- const data = actions.map(a => a.payload!.val());
+ const data = actions.map(a => a.payload.val());
expect(data).toEqual(items);
}).add(done);
ref.set(batch);
@@ -118,24 +118,22 @@ describe('snapshotChanges', () => {
});
it('should handle dynamic queries that return empty sets', done => {
- const ITEMS = 10;
let count = 0;
- let firstIndex = 0;
- let namefilter$ = new BehaviorSubject(null);
+ const namefilter$ = new BehaviorSubject(null);
const aref = createRef(rando());
aref.set(batch);
namefilter$.pipe(switchMap(name => {
- const filteredRef = name ? aref.child('name').equalTo(name) : aref
+ const filteredRef = name ? aref.child('name').equalTo(name) : aref;
return snapshotChanges(filteredRef);
- }),take(2)).subscribe(data => {
+ }), take(2)).subscribe(data => {
count = count + 1;
// the first time should all be 'added'
- if(count === 1) {
+ if (count === 1) {
expect(Object.keys(data).length).toEqual(3);
namefilter$.next(-1);
}
// on the second round, we should have filtered out everything
- if(count === 2) {
+ if (count === 2) {
expect(Object.keys(data).length).toEqual(0);
}
}).add(done);
diff --git a/src/database/list/snapshot-changes.ts b/src/database/list/snapshot-changes.ts
index 2dbb731b2..7422c65bd 100644
--- a/src/database/list/snapshot-changes.ts
+++ b/src/database/list/snapshot-changes.ts
@@ -1,9 +1,13 @@
import { Observable, SchedulerLike } from 'rxjs';
import { listChanges } from './changes';
-import { DatabaseQuery, ChildEvent, SnapshotAction } from '../interfaces';
+import { ChildEvent, DatabaseQuery, SnapshotAction } from '../interfaces';
import { validateEventsArray } from './utils';
-export function snapshotChanges(query: DatabaseQuery, events?: ChildEvent[], scheduler?: SchedulerLike): Observable[]> {
+export function snapshotChanges(
+ query: DatabaseQuery,
+ events?: ChildEvent[],
+ scheduler?: SchedulerLike
+): Observable[]> {
events = validateEventsArray(events);
- return listChanges(query, events!, scheduler);
+ return listChanges(query, events, scheduler);
}
diff --git a/src/database/list/state-changes.spec.ts b/src/database/list/state-changes.spec.ts
index ccc84f01e..b6d3c2711 100644
--- a/src/database/list/state-changes.spec.ts
+++ b/src/database/list/state-changes.spec.ts
@@ -1,7 +1,7 @@
import { database } from 'firebase/app';
-import { FirebaseApp, AngularFireModule } from '@angular/fire';
-import { AngularFireDatabase, AngularFireDatabaseModule, stateChanges, ChildEvent, URL } from '../public_api';
-import { TestBed, inject } from '@angular/core/testing';
+import { AngularFireModule, FirebaseApp } from '@angular/fire';
+import { AngularFireDatabase, AngularFireDatabaseModule, ChildEvent, stateChanges, URL } from '../public_api';
+import { TestBed } from '@angular/core/testing';
import { COMMON_CONFIG } from '../../test-config';
import { skip } from 'rxjs/operators';
import 'firebase/database';
@@ -12,10 +12,9 @@ describe('stateChanges', () => {
let db: AngularFireDatabase;
let createRef: (path: string) => database.Reference;
let batch = {};
- const items = [{ name: 'zero' }, { name: 'one' }, { name: 'two' }].map((item, i) => ( { key: i.toString(), ...item } ));
- Object.keys(items).forEach(function (key, i) {
- const itemValue = items[key];
- batch[i] = itemValue;
+ const items = [{ name: 'zero' }, { name: 'one' }, { name: 'two' }].map((item, i) => ({ key: i.toString(), ...item }));
+ Object.keys(items).forEach((key, i) => {
+ batch[i] = items[key];
});
// make batch immutable to preserve integrity
batch = Object.freeze(batch);
@@ -30,11 +29,10 @@ describe('stateChanges', () => {
{ provide: URL, useValue: 'http://localhost:9000' }
]
});
- inject([FirebaseApp, AngularFireDatabase], (app_: FirebaseApp, _db: AngularFireDatabase) => {
- app = app_;
- db = _db;
- createRef = (path: string) => db.database.ref(path);
- })();
+
+ app = TestBed.inject(FirebaseApp);
+ db = TestBed.inject(AngularFireDatabase);
+ createRef = (path: string) => db.database.ref(path);
});
afterEach(() => {
diff --git a/src/database/list/state-changes.ts b/src/database/list/state-changes.ts
index 26c460869..192043e3b 100644
--- a/src/database/list/state-changes.ts
+++ b/src/database/list/state-changes.ts
@@ -1,11 +1,10 @@
-import { DatabaseQuery, ChildEvent, AngularFireAction, SnapshotAction } from '../interfaces';
+import { ChildEvent, DatabaseQuery } from '../interfaces';
import { fromRef } from '../observable/fromRef';
import { validateEventsArray } from './utils';
-import { SchedulerLike, Observable, merge } from 'rxjs';
-import { DatabaseSnapshot } from '../interfaces';
+import { merge, SchedulerLike } from 'rxjs';
export function stateChanges(query: DatabaseQuery, events?: ChildEvent[], scheduler?: SchedulerLike) {
- events = validateEventsArray(events)!;
+ events = validateEventsArray(events);
const childEvent$ = events.map(event => fromRef(query, event, 'on', scheduler));
return merge(...childEvent$);
}
diff --git a/src/database/list/utils.ts b/src/database/list/utils.ts
index 2f1c30647..35404600c 100644
--- a/src/database/list/utils.ts
+++ b/src/database/list/utils.ts
@@ -1,8 +1,8 @@
import { isNil } from '../utils';
export function validateEventsArray(events?: any[]) {
- if(isNil(events) || events!.length === 0) {
+ if (isNil(events) || events.length === 0) {
events = ['child_added', 'child_removed', 'child_changed', 'child_moved'];
}
return events;
-}
\ No newline at end of file
+}
diff --git a/src/database/object/create-reference.ts b/src/database/object/create-reference.ts
index 8603029e5..cede58f97 100644
--- a/src/database/object/create-reference.ts
+++ b/src/database/object/create-reference.ts
@@ -1,14 +1,14 @@
import { map } from 'rxjs/operators';
-import { DatabaseQuery, AngularFireObject } from '../interfaces';
+import { AngularFireObject, DatabaseQuery } from '../interfaces';
import { createObjectSnapshotChanges } from './snapshot-changes';
import { AngularFireDatabase } from '../database';
-export function createObjectReference(query: DatabaseQuery, afDatabase: AngularFireDatabase): AngularFireObject {
+export function createObjectReference(query: DatabaseQuery, afDatabase: AngularFireDatabase): AngularFireObject {
return {
query,
snapshotChanges() {
return createObjectSnapshotChanges(query, afDatabase.schedulers.outsideAngular)().pipe(
- afDatabase.keepUnstableUntilFirst!
+ afDatabase.keepUnstableUntilFirst
);
},
update(data: Partial) { return query.ref.update(data as any) as Promise; },
@@ -19,7 +19,7 @@ export function createObjectReference(query: DatabaseQuery, afDatabase: A
return snapshotChanges$.pipe(
afDatabase.keepUnstableUntilFirst,
map(action => action.payload.exists() ? action.payload.val() as T : null)
- )
+ );
},
- }
+ };
}
diff --git a/src/database/object/snapshot-changes.ts b/src/database/object/snapshot-changes.ts
index 8da9a34d4..66ae6ef3c 100644
--- a/src/database/object/snapshot-changes.ts
+++ b/src/database/object/snapshot-changes.ts
@@ -5,5 +5,5 @@ import { DatabaseQuery, SnapshotAction } from '../interfaces';
export function createObjectSnapshotChanges(query: DatabaseQuery, scheduler?: SchedulerLike) {
return function snapshotChanges(): Observable> {
return fromRef(query, 'value', 'on', scheduler);
- }
+ };
}
diff --git a/src/database/observable/fromRef.spec.ts b/src/database/observable/fromRef.spec.ts
index 69b17ed49..e7a1f966e 100644
--- a/src/database/observable/fromRef.spec.ts
+++ b/src/database/observable/fromRef.spec.ts
@@ -1,7 +1,7 @@
import { DatabaseReference } from '../interfaces';
-import { FirebaseApp, AngularFireModule, ɵZoneScheduler } from '@angular/fire';
+import { AngularFireModule, FirebaseApp, ɵZoneScheduler } from '@angular/fire';
import { AngularFireDatabase, AngularFireDatabaseModule, fromRef } from '../public_api';
-import { TestBed, inject } from '@angular/core/testing';
+import { TestBed } from '@angular/core/testing';
import { COMMON_CONFIG } from '../../test-config';
import { take } from 'rxjs/operators';
import { TestScheduler } from 'rxjs/testing';
@@ -13,7 +13,7 @@ describe('fromRef', () => {
let ref: (path: string) => DatabaseReference;
let batch = {};
const items = [{ name: 'one' }, { name: 'two' }, { name: 'three' }].map(item => ({ key: rando(), ...item }));
- Object.keys(items).forEach(function (key) {
+ Object.keys(items).forEach((key) => {
const itemValue = items[key];
batch[itemValue.key] = itemValue;
});
@@ -27,14 +27,13 @@ describe('fromRef', () => {
AngularFireDatabaseModule
],
providers: [
- { provide: URL, useValue: 'http://localhost:9000'}
+ { provide: URL, useValue: 'http://localhost:9000' }
]
});
- inject([FirebaseApp, AngularFireDatabase], (app_: FirebaseApp, db_: AngularFireDatabase) => {
- app = app_;
- db = db_;
- ref = (path: string) => db.database.ref(path);
- })();
+
+ app = TestBed.inject(FirebaseApp);
+ db = TestBed.inject(AngularFireDatabase);
+ ref = (path: string) => db.database.ref(path);
});
afterEach(() => {
@@ -47,7 +46,7 @@ describe('fromRef', () => {
const obs = fromRef(itemRef, 'value');
let count = 0;
expect(count).toEqual(0);
- const sub = obs.subscribe(change => {
+ const sub = obs.subscribe(() => {
count = count + 1;
expect(count).toEqual(1);
sub.unsubscribe();
@@ -73,11 +72,11 @@ describe('fromRef', () => {
done();
}, err => {
console.error(err);
- expect(false).toEqual(true, "Shouldnt error");
+ expect(false).toEqual(true, 'Shouldnt error');
done();
}, () => {
expect(testScheduler.schedule).toHaveBeenCalled();
- done()
+ done();
});
testScheduler.flush();
});
@@ -90,18 +89,18 @@ describe('fromRef', () => {
// Error
const errorObservable = fromRef({
- once: (event, snap, err) => err()
- } as any,
+ once: (event, snap, err) => err()
+ } as any,
'value',
'once',
testScheduler
);
errorObservable.subscribe(() => {
- fail("Should not emit");
+ fail('Should not emit');
}, () => {
expect(testScheduler.schedule).toHaveBeenCalled();
}, () => {
- fail("Should not complete");
+ fail('Should not complete');
});
testScheduler.flush();
@@ -120,9 +119,10 @@ describe('fromRef', () => {
scheduler
);
completeObservable.subscribe(
- () => { },
- () => fail("Should not error"),
- () => expect(Zone.current.name).toEqual("ExpectedZone")
+ () => {
+ },
+ () => fail('Should not error'),
+ () => expect(Zone.current.name).toEqual('ExpectedZone')
);
testScheduler.flush();
done();
@@ -133,7 +133,7 @@ describe('fromRef', () => {
const itemRef = ref(rando());
itemRef.set({});
const obs = fromRef(itemRef, 'value');
- const sub = obs.pipe(take(1)).subscribe(change => {
+ obs.pipe(take(1)).subscribe(change => {
expect(change.payload.exists()).toEqual(false);
expect(change.payload.val()).toEqual(null);
}).add(done);
@@ -143,7 +143,9 @@ describe('fromRef', () => {
const itemRef = ref(rando());
itemRef.set(batch);
const obs = fromRef(itemRef, 'value', 'once');
- obs.subscribe(change => { }, () => { }, done);
+ obs.subscribe(() => {
+ }, () => {
+ }, done);
});
it('it should listen and then unsubscribe', (done) => {
@@ -151,7 +153,7 @@ describe('fromRef', () => {
itemRef.set(batch);
const obs = fromRef(itemRef, 'value');
let count = 0;
- const sub = obs.subscribe(change => {
+ const sub = obs.subscribe(() => {
count = count + 1;
// hard coding count to one will fail if the unsub
// doesn't actually unsub
@@ -173,7 +175,7 @@ describe('fromRef', () => {
count = count + 1;
const { type, payload } = change;
expect(type).toEqual('child_added');
- expect(payload.val()).toEqual(batch[payload.key!]);
+ expect(payload.val()).toEqual(batch[payload.key]);
if (count === items.length) {
done();
sub.unsubscribe();
@@ -230,7 +232,8 @@ describe('fromRef', () => {
sub.unsubscribe();
done();
});
- itemRef.child(key).setPriority(-100, () => { });
+ itemRef.child(key).setPriority(-100, () => {
+ });
});
it('should stream back a value event', (done: any) => {
@@ -252,9 +255,12 @@ describe('fromRef', () => {
itemRef.set(batch);
const query = itemRef.orderByChild('name').equalTo(items[0].name);
const obs = fromRef(query, 'value');
- const sub = obs.subscribe(change => {
- let child;
- change.payload.forEach(snap => { child = snap.val(); return true; });
+ obs.subscribe(change => {
+ let child = null;
+ change.payload.forEach(snap => {
+ child = snap.val();
+ return true;
+ });
expect(child).toEqual(items[0]);
done();
});
diff --git a/src/database/observable/fromRef.ts b/src/database/observable/fromRef.ts
index aac8e2ecc..d08283235 100644
--- a/src/database/observable/fromRef.ts
+++ b/src/database/observable/fromRef.ts
@@ -1,5 +1,5 @@
-import { DatabaseQuery, DatabaseSnapshot, ListenEvent, AngularFireAction } from '../interfaces';
-import { Observable, SchedulerLike, queueScheduler, asyncScheduler } from 'rxjs';
+import { AngularFireAction, DatabaseQuery, DatabaseSnapshot, ListenEvent } from '../interfaces';
+import { asyncScheduler, Observable, SchedulerLike } from 'rxjs';
import { map, share } from 'rxjs/operators';
interface SnapshotPrevKey {
@@ -11,22 +11,28 @@ interface SnapshotPrevKey {
* Create an observable from a Database Reference or Database Query.
* @param ref Database Reference
* @param event Listen event type ('value', 'added', 'changed', 'removed', 'moved')
+ * @param listenType 'on' or 'once'
+ * @param scheduler - Rxjs scheduler
*/
-export function fromRef(ref: DatabaseQuery, event: ListenEvent, listenType = 'on', scheduler: SchedulerLike = asyncScheduler): Observable>> {
+export function fromRef(ref: DatabaseQuery,
+ event: ListenEvent,
+ listenType = 'on',
+ scheduler: SchedulerLike = asyncScheduler
+): Observable>> {
return new Observable>(subscriber => {
let fn: any | null = null;
fn = ref[listenType](event, (snapshot, prevKey) => {
scheduler.schedule(() => {
subscriber.next({ snapshot, prevKey });
});
- if (listenType == 'once') {
+ if (listenType === 'once') {
scheduler.schedule(() => subscriber.complete());
}
}, err => {
- scheduler.schedule(() => subscriber.error(err))
+ scheduler.schedule(() => subscriber.error(err));
});
- if (listenType == 'on') {
+ if (listenType === 'on') {
return {
unsubscribe() {
if (fn != null) {
@@ -35,13 +41,18 @@ export function fromRef(ref: DatabaseQuery, event: ListenEvent, listenType =
}
};
} else {
- return { unsubscribe() { } };
+ return {
+ unsubscribe() {
+ }
+ };
}
}).pipe(
map(payload => {
const { snapshot, prevKey } = payload;
let key: string | null = null;
- if (snapshot.exists()) { key = snapshot.key; }
+ if (snapshot.exists()) {
+ key = snapshot.key;
+ }
return { type: event, payload: snapshot, prevKey, key };
}),
share()
diff --git a/src/database/public_api.ts b/src/database/public_api.ts
index bac23e359..b1f0ab756 100644
--- a/src/database/public_api.ts
+++ b/src/database/public_api.ts
@@ -5,4 +5,4 @@ export * from './list/snapshot-changes';
export * from './list/state-changes';
export * from './list/audit-trail';
export * from './observable/fromRef';
-export * from './database.module'
+export * from './database.module';
diff --git a/src/database/utils.spec.ts b/src/database/utils.spec.ts
index 0bc3c87c7..b437b0700 100644
--- a/src/database/utils.spec.ts
+++ b/src/database/utils.spec.ts
@@ -1,4 +1,3 @@
-import { TestBed, inject } from '@angular/core/testing';
import * as utils from './utils';
describe('utils', () => {
@@ -12,11 +11,12 @@ describe('utils', () => {
const nul = null;
const obj = {};
const fn = () => { };
- let undef;
+ const undef = undefined;
expect(utils.isString(str)).toBe(true);
expect(utils.isString(notStr)).toBe(false);
expect(utils.isString(bool)).toBe(false);
expect(utils.isString(nul)).toBe(false);
+ expect(utils.isString(obj)).toBe(false);
expect(utils.isString(fn)).toBe(false);
expect(utils.isString(undef)).toBe(false);
});
diff --git a/src/database/utils.ts b/src/database/utils.ts
index 4c0275864..8811bdbfa 100644
--- a/src/database/utils.ts
+++ b/src/database/utils.ts
@@ -1,5 +1,4 @@
-import { Subscription, Scheduler } from 'rxjs';
-import { PathReference, DatabaseReference, FirebaseOperation, FirebaseOperationCases } from './interfaces';
+import { DatabaseReference, FirebaseOperation, FirebaseOperationCases, PathReference } from './interfaces';
import { database } from 'firebase/app';
export function isString(value: any): boolean {
@@ -21,8 +20,8 @@ export function isFirebaseRef(value: any): boolean {
/**
* Returns a database reference given a Firebase App and an
* absolute or relative path.
- * @param app - Firebase App
- * @param path - Database path, relative or absolute
+ * @param database - Firebase Database
+ * @param pathRef - Database path, relative or absolute
*/
export function getRef(database: database.Database, pathRef: PathReference): DatabaseReference {
// if a db ref was passed in, just return it
@@ -30,13 +29,13 @@ export function getRef(database: database.Database, pathRef: PathReference): Dat
: database.ref(pathRef as string);
}
-export function checkOperationCases(item: FirebaseOperation, cases: FirebaseOperationCases) : Promise {
+export function checkOperationCases(item: FirebaseOperation, cases: FirebaseOperationCases): Promise {
if (isString(item)) {
return cases.stringCase();
} else if (isFirebaseRef(item)) {
- return cases.firebaseCase!();
+ return cases.firebaseCase();
} else if (isFirebaseDataSnapshot(item)) {
- return cases.snapshotCase!();
+ return cases.snapshotCase();
}
throw new Error(`Expects a string, snapshot, or reference. Got: ${typeof item}`);
}
diff --git a/src/firestore/collection-group/collection-group.spec.ts b/src/firestore/collection-group/collection-group.spec.ts
index 24d9fdf25..4c199fb17 100644
--- a/src/firestore/collection-group/collection-group.spec.ts
+++ b/src/firestore/collection-group/collection-group.spec.ts
@@ -1,13 +1,23 @@
-import { FirebaseApp, AngularFireModule } from '@angular/fire';
-import { AngularFirestore, AngularFirestoreModule, AngularFirestoreDocument, AngularFirestoreCollectionGroup, SETTINGS } from '../public_api';
-import { QueryGroupFn, Query } from '../interfaces';
-import { Observable, BehaviorSubject, Subscription } from 'rxjs';
-import { skip, take, switchMap } from 'rxjs/operators';
-import { TestBed, inject } from '@angular/core/testing';
+import { AngularFireModule, FirebaseApp } from '@angular/fire';
+import { AngularFirestore, AngularFirestoreCollectionGroup, AngularFirestoreModule, SETTINGS } from '../public_api';
+import { Query, QueryGroupFn } from '../interfaces';
+import { BehaviorSubject } from 'rxjs';
+import { skip, switchMap, take } from 'rxjs/operators';
+import { TestBed } from '@angular/core/testing';
import { COMMON_CONFIG } from '../../test-config';
import 'firebase/firestore';
-import { Stock, randomName, FAKE_STOCK_DATA, createRandomStocks, delayAdd, delayDelete, delayUpdate, deleteThemAll, rando } from '../utils.spec';
+import {
+ createRandomStocks,
+ delayAdd,
+ delayDelete,
+ delayUpdate,
+ deleteThemAll,
+ FAKE_STOCK_DATA,
+ rando,
+ randomName,
+ Stock
+} from '../utils.spec';
async function collectionHarness(afs: AngularFirestore, items: number, queryGroupFn?: QueryGroupFn) {
const randomCollectionName = randomName(afs.firestore);
@@ -16,14 +26,13 @@ async function collectionHarness(afs: AngularFirestore, items: number, queryGrou
const collectionGroup: Query = firestore.collectionGroup(randomCollectionName);
const queryFn = queryGroupFn || (ref => ref);
const stocks = new AngularFirestoreCollectionGroup(queryFn(collectionGroup), afs);
- let names = await createRandomStocks(afs.firestore, ref, items);
+ const names = await createRandomStocks(afs.firestore, ref, items);
return { randomCollectionName, ref, stocks, names };
}
describe('AngularFirestoreCollectionGroup', () => {
let app: FirebaseApp;
let afs: AngularFirestore;
- let sub: Subscription;
beforeEach(() => {
TestBed.configureTestingModule({
@@ -35,10 +44,9 @@ describe('AngularFirestoreCollectionGroup', () => {
{ provide: SETTINGS, useValue: { host: 'localhost:8080', ssl: false } }
]
});
- inject([FirebaseApp, AngularFirestore], (_app: FirebaseApp, _afs: AngularFirestore) => {
- app = _app;
- afs = _afs;
- })();
+
+ app = TestBed.inject(FirebaseApp);
+ afs = TestBed.inject(AngularFirestore);
});
afterEach(() => {
@@ -49,7 +57,7 @@ describe('AngularFirestoreCollectionGroup', () => {
it('should get unwrapped snapshot', async (done: any) => {
const ITEMS = 4;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const sub = stocks.valueChanges().subscribe(data => {
// unsub immediately as we will be deleting data at the bottom
@@ -73,9 +81,10 @@ describe('AngularFirestoreCollectionGroup', () => {
it('should handle multiple subscriptions (hot)', async (done: any) => {
const ITEMS = 4;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const changes = stocks.valueChanges();
- const sub = changes.subscribe(() => {}).add(
+ const sub = changes.subscribe(() => {
+ }).add(
changes.pipe(take(1)).subscribe(data => {
expect(data.length).toEqual(ITEMS);
sub.unsubscribe();
@@ -87,10 +96,11 @@ describe('AngularFirestoreCollectionGroup', () => {
it('should handle multiple subscriptions (warm)', async (done: any) => {
const ITEMS = 4;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const changes = stocks.valueChanges();
- changes.pipe(take(1)).subscribe(() => {}).add(() => {
- const sub = changes.pipe(take(1)).subscribe(data => {
+ changes.pipe(take(1)).subscribe(() => {
+ }).add(() => {
+ changes.pipe(take(1)).subscribe(data => {
expect(data.length).toEqual(ITEMS);
}).add(() => {
deleteThemAll(names, ref).then(done).catch(done.fail);
@@ -101,22 +111,22 @@ describe('AngularFirestoreCollectionGroup', () => {
it('should handle dynamic queries that return empty sets', async (done) => {
const ITEMS = 10;
let count = 0;
- let firstIndex = 0;
- let pricefilter$ = new BehaviorSubject(null);
+
+ const pricefilter$ = new BehaviorSubject(null);
const randomCollectionName = randomName(afs.firestore);
const ref = afs.firestore.collection(`${randomCollectionName}`);
- let names = await createRandomStocks(afs.firestore, ref, ITEMS);
+ const names = await createRandomStocks(afs.firestore, ref, ITEMS);
const sub = pricefilter$.pipe(switchMap(price => {
- return afs.collection(randomCollectionName, ref => price ? ref.where('price', '==', price) : ref).valueChanges()
+ return afs.collection(randomCollectionName, ref => price ? ref.where('price', '==', price) : ref).valueChanges();
})).subscribe(data => {
count = count + 1;
// the first time should all be 'added'
- if(count === 1) {
+ if (count === 1) {
expect(data.length).toEqual(ITEMS);
pricefilter$.next(-1);
}
// on the second round, we should have filtered out everything
- if(count === 2) {
+ if (count === 2) {
expect(data.length).toEqual(0);
sub.unsubscribe();
deleteThemAll(names, ref).then(done).catch(done.fail);
@@ -131,18 +141,17 @@ describe('AngularFirestoreCollectionGroup', () => {
it('should listen to all snapshotChanges() by default', async (done) => {
const ITEMS = 10;
let count = 0;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const sub = stocks.snapshotChanges().subscribe(data => {
- const ids = data.map(d => d.payload.doc.id);
count = count + 1;
// the first time should all be 'added'
- if(count === 1) {
+ if (count === 1) {
// make an update
- ref.doc(names[0]).update({ price: 2});
+ ref.doc(names[0]).update({ price: 2 });
}
// on the second round, make sure the array is still the same
// length but the updated item is now modified
- if(count === 2) {
+ if (count === 2) {
expect(data.length).toEqual(ITEMS);
const change = data.filter(x => x.payload.doc.id === names[0])[0];
expect(change.type).toEqual('modified');
@@ -154,9 +163,10 @@ describe('AngularFirestoreCollectionGroup', () => {
it('should handle multiple subscriptions (hot)', async (done: any) => {
const ITEMS = 4;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const changes = stocks.snapshotChanges();
- const sub = changes.subscribe(() => {}).add(
+ const sub = changes.subscribe(() => {
+ }).add(
changes.pipe(take(1)).subscribe(data => {
expect(data.length).toEqual(ITEMS);
sub.unsubscribe();
@@ -168,10 +178,11 @@ describe('AngularFirestoreCollectionGroup', () => {
it('should handle multiple subscriptions (warm)', async (done: any) => {
const ITEMS = 4;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const changes = stocks.snapshotChanges();
- changes.pipe(take(1)).subscribe(() => {}).add(() => {
- const sub = changes.pipe(take(1)).subscribe(data => {
+ changes.pipe(take(1)).subscribe(() => {
+ }).add(() => {
+ changes.pipe(take(1)).subscribe(data => {
expect(data.length).toEqual(ITEMS);
}).add(() => {
deleteThemAll(names, ref).then(done).catch(done.fail);
@@ -183,19 +194,19 @@ describe('AngularFirestoreCollectionGroup', () => {
const ITEMS = 10;
let count = 0;
let firstIndex = 0;
- const { randomCollectionName, ref, stocks, names } =
+ const { ref, stocks, names } =
await collectionHarness(afs, ITEMS, ref => ref.orderBy('price', 'desc'));
const sub = stocks.snapshotChanges().subscribe(data => {
count = count + 1;
// the first time should all be 'added'
- if(count === 1) {
+ if (count === 1) {
// make an update
firstIndex = data.filter(d => d.payload.doc.id === names[0])[0].payload.newIndex;
ref.doc(names[0]).update({ price: 2 });
}
// on the second round, make sure the array is still the same
// length but the updated item is now modified
- if(count === 2) {
+ if (count === 2) {
expect(data.length).toEqual(ITEMS);
const change = data.filter(x => x.payload.doc.id === names[0])[0];
expect(change.type).toEqual('modified');
@@ -208,7 +219,7 @@ describe('AngularFirestoreCollectionGroup', () => {
it('should be able to filter snapshotChanges() types - modified', async (done) => {
const ITEMS = 10;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const sub = stocks.snapshotChanges(['modified']).pipe(skip(1)).subscribe(data => {
sub.unsubscribe();
@@ -224,7 +235,9 @@ describe('AngularFirestoreCollectionGroup', () => {
it('should be able to filter snapshotChanges() types - added', async (done) => {
const ITEMS = 10;
- let { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const harness = await collectionHarness(afs, ITEMS);
+ const { randomCollectionName, ref, stocks } = harness;
+ let { names } = harness;
const nextId = ref.doc('a').id;
const sub = stocks.snapshotChanges(['added']).pipe(skip(1)).subscribe(data => {
@@ -246,8 +259,8 @@ describe('AngularFirestoreCollectionGroup', () => {
it('should be able to filter snapshotChanges() types - added w/same id', async (done) => {
const ITEMS = 10;
- let { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
-
+ const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+
const sub = stocks.snapshotChanges(['added']).pipe(skip(1)).subscribe(data => {
sub.unsubscribe();
const change = data.filter(x => x.payload.doc.id === names[0])[1];
@@ -265,20 +278,24 @@ describe('AngularFirestoreCollectionGroup', () => {
it('should be able to filter snapshotChanges() types - added/modified', async (done) => {
const ITEMS = 10;
- let { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+
+ const harness = await collectionHarness(afs, ITEMS);
+ const { ref, stocks } = harness;
+ let { names } = harness;
+
const nextId = ref.doc('a').id;
let count = 0;
- const sub = stocks.snapshotChanges(['added', 'modified']).pipe(skip(1),take(2)).subscribe(data => {
+ stocks.snapshotChanges(['added', 'modified']).pipe(skip(1), take(2)).subscribe(data => {
count += 1;
- if (count == 1) {
+ if (count === 1) {
const change = data.filter(x => x.payload.doc.id === nextId)[0];
expect(data.length).toEqual(ITEMS + 1);
expect(change.payload.doc.data().price).toEqual(2);
expect(change.type).toEqual('added');
delayUpdate(ref, names[0], { price: 2 });
}
- if (count == 2) {
+ if (count === 2) {
const change = data.filter(x => x.payload.doc.id === names[0])[0];
expect(data.length).toEqual(ITEMS + 1);
expect(change.payload.doc.data().price).toEqual(2);
@@ -294,7 +311,7 @@ describe('AngularFirestoreCollectionGroup', () => {
it('should be able to filter snapshotChanges() types - removed', async (done) => {
const ITEMS = 10;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const sub = stocks.snapshotChanges(['added', 'removed']).pipe(skip(1)).subscribe(data => {
sub.unsubscribe();
@@ -314,7 +331,7 @@ describe('AngularFirestoreCollectionGroup', () => {
it('should get stateChanges() updates', async (done: any) => {
const ITEMS = 10;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const sub = stocks.stateChanges().subscribe(data => {
// unsub immediately as we will be deleting data at the bottom
@@ -337,13 +354,13 @@ describe('AngularFirestoreCollectionGroup', () => {
it('should listen to all stateChanges() by default', async (done) => {
const ITEMS = 10;
let count = 0;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
- const sub = stocks.stateChanges().subscribe(data => {
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ stocks.stateChanges().subscribe(data => {
count = count + 1;
- if(count === 1) {
- ref.doc(names[0]).update({ price: 2});
+ if (count === 1) {
+ ref.doc(names[0]).update({ price: 2 });
}
- if(count === 2) {
+ if (count === 2) {
expect(data.length).toEqual(1);
expect(data[0].type).toEqual('modified');
deleteThemAll(names, ref).then(done).catch(done.fail);
@@ -353,9 +370,10 @@ describe('AngularFirestoreCollectionGroup', () => {
it('should handle multiple subscriptions (hot)', async (done: any) => {
const ITEMS = 4;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const changes = stocks.stateChanges();
- const sub = changes.subscribe(() => {}).add(
+ const sub = changes.subscribe(() => {
+ }).add(
changes.pipe(take(1)).subscribe(data => {
expect(data.length).toEqual(ITEMS);
sub.unsubscribe();
@@ -367,10 +385,11 @@ describe('AngularFirestoreCollectionGroup', () => {
it('should handle multiple subscriptions (warm)', async (done: any) => {
const ITEMS = 4;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const changes = stocks.stateChanges();
- changes.pipe(take(1)).subscribe(() => {}).add(() => {
- const sub = changes.pipe(take(1)).subscribe(data => {
+ changes.pipe(take(1)).subscribe(() => {
+ }).add(() => {
+ changes.pipe(take(1)).subscribe(data => {
expect(data.length).toEqual(ITEMS);
}).add(() => {
deleteThemAll(names, ref).then(done).catch(done.fail);
@@ -380,8 +399,7 @@ describe('AngularFirestoreCollectionGroup', () => {
it('should be able to filter stateChanges() types - modified', async (done) => {
const ITEMS = 10;
- let count = 0;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const sub = stocks.stateChanges(['modified']).subscribe(data => {
sub.unsubscribe();
@@ -397,8 +415,11 @@ describe('AngularFirestoreCollectionGroup', () => {
it('should be able to filter stateChanges() types - added', async (done) => {
const ITEMS = 10;
- let count = 0;
- let { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+
+ const harness = await collectionHarness(afs, ITEMS);
+ const { ref, stocks } = harness;
+ let { names } = harness;
+
const sub = stocks.stateChanges(['added']).pipe(skip(1)).subscribe(data => {
sub.unsubscribe();
@@ -416,7 +437,7 @@ describe('AngularFirestoreCollectionGroup', () => {
it('should be able to filter stateChanges() types - removed', async (done) => {
const ITEMS = 10;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const sub = stocks.stateChanges(['removed']).subscribe(data => {
sub.unsubscribe();
@@ -434,13 +455,13 @@ describe('AngularFirestoreCollectionGroup', () => {
it('should listen to all events for auditTrail() by default', async (done) => {
const ITEMS = 10;
let count = 0;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const sub = stocks.auditTrail().subscribe(data => {
count = count + 1;
- if(count === 1) {
- ref.doc(names[0]).update({ price: 2});
+ if (count === 1) {
+ ref.doc(names[0]).update({ price: 2 });
}
- if(count === 2) {
+ if (count === 2) {
sub.unsubscribe();
expect(data.length).toEqual(ITEMS + 1);
expect(data[data.length - 1].type).toEqual('modified');
@@ -451,7 +472,7 @@ describe('AngularFirestoreCollectionGroup', () => {
it('should be able to filter auditTrail() types - removed', async (done) => {
const ITEMS = 10;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const sub = stocks.auditTrail(['removed']).subscribe(data => {
sub.unsubscribe();
diff --git a/src/firestore/collection-group/collection-group.ts b/src/firestore/collection-group/collection-group.ts
index b20942d14..1c3aff9fd 100644
--- a/src/firestore/collection-group/collection-group.ts
+++ b/src/firestore/collection-group/collection-group.ts
@@ -1,9 +1,9 @@
-import { Observable, from } from 'rxjs';
+import { from, Observable } from 'rxjs';
import { fromCollectionRef } from '../observable/fromRef';
-import { map, filter, scan, observeOn } from 'rxjs/operators';
+import { filter, map, observeOn, scan } from 'rxjs/operators';
import { firestore } from 'firebase/app';
-import { DocumentChangeType, Query, DocumentData, DocumentChangeAction } from '../interfaces';
+import { DocumentChangeAction, DocumentChangeType, DocumentData, Query } from '../interfaces';
import { validateEventsArray } from '../collection/collection';
import { docChanges, sortedChanges } from '../collection/changes';
import { AngularFirestore } from '../firestore';
@@ -26,12 +26,10 @@ import { AngularFirestore } from '../firestore';
* // Subscribe to changes as snapshots. This provides you data updates as well as delta updates.
* fakeStock.valueChanges().subscribe(value => console.log(value));
*/
-export class AngularFirestoreCollectionGroup {
+export class AngularFirestoreCollectionGroup {
/**
* The constructor takes in a CollectionGroupQuery to provide wrapper methods
* for data operations and data streaming.
- * @param query
- * @param afs
*/
constructor(
private readonly query: Query,
@@ -41,10 +39,9 @@ export class AngularFirestoreCollectionGroup {
* Listen to the latest change in the stream. This method returns changes
* as they occur and they are not sorted by query order. This allows you to construct
* your own data structure.
- * @param events
*/
stateChanges(events?: DocumentChangeType[]): Observable[]> {
- if(!events || events.length === 0) {
+ if (!events || events.length === 0) {
return docChanges(this.query, this.afs.schedulers.outsideAngular).pipe(
this.afs.keepUnstableUntilFirst
);
@@ -60,7 +57,6 @@ export class AngularFirestoreCollectionGroup {
/**
* Create a stream of changes as they occur it time. This method is similar to stateChanges()
* but it collects each event in an array over time.
- * @param events
*/
auditTrail(events?: DocumentChangeType[]): Observable[]> {
return this.stateChanges(events).pipe(scan((current, action) => [...current, ...action], []));
@@ -69,7 +65,6 @@ export class AngularFirestoreCollectionGroup {
/**
* Create a stream of synchronized changes. This method keeps the local array in sorted
* query order.
- * @param events
*/
snapshotChanges(events?: DocumentChangeType[]): Observable[]> {
const validatedEvents = validateEventsArray(events);
@@ -93,7 +88,6 @@ export class AngularFirestoreCollectionGroup {
/**
* Retrieve the results of the query once.
- * @param options
*/
get(options?: firestore.GetOptions) {
return from(this.query.get(options)).pipe(
diff --git a/src/firestore/collection/changes.ts b/src/firestore/collection/changes.ts
index d42ab0b86..07f0d12ba 100644
--- a/src/firestore/collection/changes.ts
+++ b/src/firestore/collection/changes.ts
@@ -2,12 +2,11 @@ import { fromCollectionRef } from '../observable/fromRef';
import { Observable, SchedulerLike } from 'rxjs';
import { map, scan } from 'rxjs/operators';
-import { Query, DocumentChangeType, DocumentChange, DocumentChangeAction } from '../interfaces';
+import { DocumentChange, DocumentChangeAction, DocumentChangeType, Query } from '../interfaces';
/**
* Return a stream of document changes on a query. These results are not in sort order but in
* order of occurence.
- * @param query
*/
export function docChanges(query: Query, scheduler?: SchedulerLike): Observable[]> {
return fromCollectionRef(query, scheduler)
@@ -19,9 +18,11 @@ export function docChanges(query: Query, scheduler?: SchedulerLike): Observab
/**
* Return a stream of document changes on a query. These results are in sort order.
- * @param query
*/
-export function sortedChanges(query: Query, events: DocumentChangeType[], scheduler?: SchedulerLike): Observable[]> {
+export function sortedChanges(
+ query: Query,
+ events: DocumentChangeType[],
+ scheduler?: SchedulerLike): Observable[]> {
return fromCollectionRef(query, scheduler)
.pipe(
map(changes => changes.payload.docChanges()),
@@ -32,14 +33,11 @@ export function sortedChanges(query: Query, events: DocumentChangeType[], sch
/**
* Combines the total result set from the current set of changes from an incoming set
* of changes.
- * @param current
- * @param changes
- * @param events
*/
export function combineChanges(current: DocumentChange[], changes: DocumentChange[], events: DocumentChangeType[]) {
changes.forEach(change => {
// skip unwanted change types
- if(events.indexOf(change.type) > -1) {
+ if (events.indexOf(change.type) > -1) {
current = combineChange(current, change);
}
});
@@ -48,11 +46,9 @@ export function combineChanges(current: DocumentChange[], changes: Documen
/**
* Creates a new sorted array from a new change.
- * @param combined
- * @param change
*/
export function combineChange(combined: DocumentChange[], change: DocumentChange): DocumentChange[] {
- switch(change.type) {
+ switch (change.type) {
case 'added':
if (combined[change.newIndex] && combined[change.newIndex].doc.ref.isEqual(change.doc.ref)) {
// Not sure why the duplicates are getting fired
@@ -64,7 +60,7 @@ export function combineChange(combined: DocumentChange[], change: Document
if (combined[change.oldIndex] == null || combined[change.oldIndex].doc.ref.isEqual(change.doc.ref)) {
// When an item changes position we first remove it
// and then add it's new position
- if(change.oldIndex !== change.newIndex) {
+ if (change.oldIndex !== change.newIndex) {
combined.splice(change.oldIndex, 1);
combined.splice(change.newIndex, 0, change);
} else {
diff --git a/src/firestore/collection/collection.spec.ts b/src/firestore/collection/collection.spec.ts
index 864b22ba4..5d1951a59 100644
--- a/src/firestore/collection/collection.spec.ts
+++ b/src/firestore/collection/collection.spec.ts
@@ -1,30 +1,41 @@
-import { FirebaseApp, AngularFireModule } from '@angular/fire';
+import { AngularFireModule, FirebaseApp } from '@angular/fire';
import { AngularFirestore, SETTINGS } from '../firestore';
import { AngularFirestoreModule } from '../firestore.module';
import { AngularFirestoreCollection } from './collection';
import { QueryFn } from '../interfaces';
-import { Observable, BehaviorSubject, Subscription } from 'rxjs';
-import { skip, take, switchMap } from 'rxjs/operators';
+import { BehaviorSubject } from 'rxjs';
+import { skip, switchMap, take } from 'rxjs/operators';
import 'firebase/firestore';
-import { TestBed, inject } from '@angular/core/testing';
+import { TestBed } from '@angular/core/testing';
import { COMMON_CONFIG } from '../../test-config';
-import { Stock, randomName, FAKE_STOCK_DATA, createRandomStocks, delayAdd, delayDelete, delayUpdate, deleteThemAll, rando } from '../utils.spec';
+import {
+ createRandomStocks,
+ delayAdd,
+ delayDelete,
+ delayUpdate,
+ deleteThemAll,
+ FAKE_STOCK_DATA,
+ rando,
+ randomName,
+ Stock
+} from '../utils.spec';
async function collectionHarness(afs: AngularFirestore, items: number, queryFn?: QueryFn) {
const randomCollectionName = randomName(afs.firestore);
const ref = afs.firestore.collection(`${randomCollectionName}`);
- if(!queryFn) { queryFn = (ref) => ref; }
+ if (!queryFn) {
+ queryFn = (ref) => ref;
+ }
const stocks = new AngularFirestoreCollection(ref, queryFn(ref), afs);
- let names = await createRandomStocks(afs.firestore, ref, items);
+ const names = await createRandomStocks(afs.firestore, ref, items);
return { randomCollectionName, ref, stocks, names };
}
describe('AngularFirestoreCollection', () => {
let app: FirebaseApp;
let afs: AngularFirestore;
- let sub: Subscription;
beforeEach(() => {
TestBed.configureTestingModule({
@@ -36,10 +47,9 @@ describe('AngularFirestoreCollection', () => {
{ provide: SETTINGS, useValue: { host: 'localhost:8080', ssl: false } }
]
});
- inject([FirebaseApp, AngularFirestore], (_app: FirebaseApp, _afs: AngularFirestore) => {
- app = _app;
- afs = _afs;
- })();
+
+ app = TestBed.inject(FirebaseApp);
+ afs = TestBed.inject(AngularFirestore);
});
afterEach(() => {
@@ -50,7 +60,7 @@ describe('AngularFirestoreCollection', () => {
it('should get unwrapped snapshot', async (done: any) => {
const ITEMS = 4;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const sub = stocks.valueChanges().subscribe(data => {
// unsub immediately as we will be deleting data at the bottom
@@ -72,25 +82,26 @@ describe('AngularFirestoreCollection', () => {
});
-/* FLAKE? timing out
- it('should optionally map the doc ID to the emitted data object', async (done: any) => {
- const ITEMS = 1;
- const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
- const idField = 'myCustomID';
- const sub = stocks.valueChanges({idField}).subscribe(data => {
- sub.unsubscribe();
- const stock = data[0];
- expect(stock[idField]).toBeDefined();
- expect(stock).toEqual(jasmine.objectContaining(FAKE_STOCK_DATA));
- deleteThemAll(names, ref).then(done).catch(fail);
- })
- });*/
+ /* FLAKE? timing out
+ it('should optionally map the doc ID to the emitted data object', async (done: any) => {
+ const ITEMS = 1;
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const idField = 'myCustomID';
+ const sub = stocks.valueChanges({idField}).subscribe(data => {
+ sub.unsubscribe();
+ const stock = data[0];
+ expect(stock[idField]).toBeDefined();
+ expect(stock).toEqual(jasmine.objectContaining(FAKE_STOCK_DATA));
+ deleteThemAll(names, ref).then(done).catch(fail);
+ })
+ });*/
it('should handle multiple subscriptions (hot)', async (done: any) => {
const ITEMS = 4;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const changes = stocks.valueChanges();
- const sub = changes.subscribe(() => {}).add(
+ const sub = changes.subscribe(() => {
+ }).add(
changes.pipe(take(1)).subscribe(data => {
expect(data.length).toEqual(ITEMS);
sub.unsubscribe();
@@ -102,10 +113,11 @@ describe('AngularFirestoreCollection', () => {
it('should handle multiple subscriptions (warm)', async (done: any) => {
const ITEMS = 4;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const changes = stocks.valueChanges();
- changes.pipe(take(1)).subscribe(() => {}).add(() => {
- const sub = changes.pipe(take(1)).subscribe(data => {
+ changes.pipe(take(1)).subscribe(() => {
+ }).add(() => {
+ changes.pipe(take(1)).subscribe(data => {
expect(data.length).toEqual(ITEMS);
}).add(() => {
deleteThemAll(names, ref).then(done).catch(done.fail);
@@ -116,22 +128,22 @@ describe('AngularFirestoreCollection', () => {
it('should handle dynamic queries that return empty sets', async (done) => {
const ITEMS = 10;
let count = 0;
- let firstIndex = 0;
- let pricefilter$ = new BehaviorSubject(null);
+
+ const pricefilter$ = new BehaviorSubject(null);
const randomCollectionName = randomName(afs.firestore);
const ref = afs.firestore.collection(`${randomCollectionName}`);
- let names = await createRandomStocks(afs.firestore, ref, ITEMS);
+ const names = await createRandomStocks(afs.firestore, ref, ITEMS);
const sub = pricefilter$.pipe(switchMap(price => {
- return afs.collection(randomCollectionName, ref => price ? ref.where('price', '==', price) : ref).valueChanges()
+ return afs.collection(randomCollectionName, ref => price ? ref.where('price', '==', price) : ref).valueChanges();
})).subscribe(data => {
count = count + 1;
// the first time should all be 'added'
- if(count === 1) {
+ if (count === 1) {
expect(data.length).toEqual(ITEMS);
pricefilter$.next(-1);
}
// on the second round, we should have filtered out everything
- if(count === 2) {
+ if (count === 2) {
expect(data.length).toEqual(0);
sub.unsubscribe();
deleteThemAll(names, ref).then(done).catch(done.fail);
@@ -146,18 +158,17 @@ describe('AngularFirestoreCollection', () => {
it('should listen to all snapshotChanges() by default', async (done) => {
const ITEMS = 10;
let count = 0;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const sub = stocks.snapshotChanges().subscribe(data => {
- const ids = data.map(d => d.payload.doc.id);
count = count + 1;
// the first time should all be 'added'
- if(count === 1) {
+ if (count === 1) {
// make an update
- stocks.doc(names[0]).update({ price: 2});
+ stocks.doc(names[0]).update({ price: 2 });
}
// on the second round, make sure the array is still the same
// length but the updated item is now modified
- if(count === 2) {
+ if (count === 2) {
expect(data.length).toEqual(ITEMS);
const change = data.filter(x => x.payload.doc.id === names[0])[0];
expect(change.type).toEqual('modified');
@@ -169,9 +180,10 @@ describe('AngularFirestoreCollection', () => {
it('should handle multiple subscriptions (hot)', async (done: any) => {
const ITEMS = 4;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const changes = stocks.snapshotChanges();
- const sub = changes.subscribe(() => {}).add(
+ const sub = changes.subscribe(() => {
+ }).add(
changes.pipe(take(1)).subscribe(data => {
expect(data.length).toEqual(ITEMS);
sub.unsubscribe();
@@ -183,10 +195,11 @@ describe('AngularFirestoreCollection', () => {
it('should handle multiple subscriptions (warm)', async (done: any) => {
const ITEMS = 4;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const changes = stocks.snapshotChanges();
- changes.pipe(take(1)).subscribe(() => {}).add(() => {
- const sub = changes.pipe(take(1)).subscribe(data => {
+ changes.pipe(take(1)).subscribe(() => {
+ }).add(() => {
+ changes.pipe(take(1)).subscribe(data => {
expect(data.length).toEqual(ITEMS);
}).add(() => {
deleteThemAll(names, ref).then(done).catch(done.fail);
@@ -198,19 +211,19 @@ describe('AngularFirestoreCollection', () => {
const ITEMS = 10;
let count = 0;
let firstIndex = 0;
- const { randomCollectionName, ref, stocks, names } =
+ const { ref, stocks, names } =
await collectionHarness(afs, ITEMS, ref => ref.orderBy('price', 'desc'));
const sub = stocks.snapshotChanges().subscribe(data => {
count = count + 1;
// the first time should all be 'added'
- if(count === 1) {
+ if (count === 1) {
// make an update
firstIndex = data.filter(d => d.payload.doc.id === names[0])[0].payload.newIndex;
stocks.doc(names[0]).update({ price: 2 });
}
// on the second round, make sure the array is still the same
// length but the updated item is now modified
- if(count === 2) {
+ if (count === 2) {
expect(data.length).toEqual(ITEMS);
const change = data.filter(x => x.payload.doc.id === names[0])[0];
expect(change.type).toEqual('modified');
@@ -223,7 +236,7 @@ describe('AngularFirestoreCollection', () => {
it('should be able to filter snapshotChanges() types - modified', async (done) => {
const ITEMS = 10;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const sub = stocks.snapshotChanges(['modified']).pipe(skip(1)).subscribe(data => {
sub.unsubscribe();
@@ -239,7 +252,10 @@ describe('AngularFirestoreCollection', () => {
it('should be able to filter snapshotChanges() types - added', async (done) => {
const ITEMS = 10;
- let { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const harness = await collectionHarness(afs, ITEMS);
+ const { ref, stocks } = harness;
+ let names = harness.names;
+
const nextId = ref.doc('a').id;
const sub = stocks.snapshotChanges(['added']).pipe(skip(1)).subscribe(data => {
@@ -259,20 +275,23 @@ describe('AngularFirestoreCollection', () => {
it('should be able to filter snapshotChanges() types - added/modified', async (done) => {
const ITEMS = 10;
- let { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const harness = await collectionHarness(afs, ITEMS);
+ const { ref, stocks } = harness;
+ let names = harness.names;
+
const nextId = ref.doc('a').id;
let count = 0;
- const sub = stocks.snapshotChanges(['added', 'modified']).pipe(skip(1),take(2)).subscribe(data => {
+ stocks.snapshotChanges(['added', 'modified']).pipe(skip(1), take(2)).subscribe(data => {
count += 1;
- if (count == 1) {
+ if (count === 1) {
const change = data.filter(x => x.payload.doc.id === nextId)[0];
expect(data.length).toEqual(ITEMS + 1);
expect(change.payload.doc.data().price).toEqual(2);
expect(change.type).toEqual('added');
delayUpdate(stocks, names[0], { price: 2 });
}
- if (count == 2) {
+ if (count === 2) {
const change = data.filter(x => x.payload.doc.id === names[0])[0];
expect(data.length).toEqual(ITEMS + 1);
expect(change.payload.doc.data().price).toEqual(2);
@@ -288,7 +307,7 @@ describe('AngularFirestoreCollection', () => {
it('should be able to filter snapshotChanges() types - removed', async (done) => {
const ITEMS = 10;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const sub = stocks.snapshotChanges(['added', 'removed']).pipe(skip(1)).subscribe(data => {
sub.unsubscribe();
@@ -308,7 +327,7 @@ describe('AngularFirestoreCollection', () => {
it('should get stateChanges() updates', async (done: any) => {
const ITEMS = 10;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const sub = stocks.stateChanges().subscribe(data => {
// unsub immediately as we will be deleting data at the bottom
@@ -331,13 +350,13 @@ describe('AngularFirestoreCollection', () => {
it('should listen to all stateChanges() by default', async (done) => {
const ITEMS = 10;
let count = 0;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
- const sub = stocks.stateChanges().subscribe(data => {
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ stocks.stateChanges().subscribe(data => {
count = count + 1;
- if(count === 1) {
- stocks.doc(names[0]).update({ price: 2});
+ if (count === 1) {
+ stocks.doc(names[0]).update({ price: 2 });
}
- if(count === 2) {
+ if (count === 2) {
expect(data.length).toEqual(1);
expect(data[0].type).toEqual('modified');
deleteThemAll(names, ref).then(done).catch(done.fail);
@@ -347,9 +366,10 @@ describe('AngularFirestoreCollection', () => {
it('should handle multiple subscriptions (hot)', async (done: any) => {
const ITEMS = 4;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const changes = stocks.stateChanges();
- const sub = changes.subscribe(() => {}).add(
+ const sub = changes.subscribe(() => {
+ }).add(
changes.pipe(take(1)).subscribe(data => {
expect(data.length).toEqual(ITEMS);
sub.unsubscribe();
@@ -361,10 +381,11 @@ describe('AngularFirestoreCollection', () => {
it('should handle multiple subscriptions (warm)', async (done: any) => {
const ITEMS = 4;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const changes = stocks.stateChanges();
- changes.pipe(take(1)).subscribe(() => {}).add(() => {
- const sub = changes.pipe(take(1)).subscribe(data => {
+ changes.pipe(take(1)).subscribe(() => {
+ }).add(() => {
+ changes.pipe(take(1)).subscribe(data => {
expect(data.length).toEqual(ITEMS);
}).add(() => {
deleteThemAll(names, ref).then(done).catch(done.fail);
@@ -374,8 +395,7 @@ describe('AngularFirestoreCollection', () => {
it('should be able to filter stateChanges() types - modified', async (done) => {
const ITEMS = 10;
- let count = 0;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const sub = stocks.stateChanges(['modified']).subscribe(data => {
sub.unsubscribe();
@@ -391,8 +411,10 @@ describe('AngularFirestoreCollection', () => {
it('should be able to filter stateChanges() types - added', async (done) => {
const ITEMS = 10;
- let count = 0;
- let { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+
+ const harness = await collectionHarness(afs, ITEMS);
+ const { ref, stocks } = harness;
+ let names = harness.names;
const sub = stocks.stateChanges(['added']).pipe(skip(1)).subscribe(data => {
sub.unsubscribe();
@@ -410,7 +432,7 @@ describe('AngularFirestoreCollection', () => {
it('should be able to filter stateChanges() types - removed', async (done) => {
const ITEMS = 10;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const sub = stocks.stateChanges(['removed']).subscribe(data => {
sub.unsubscribe();
@@ -428,13 +450,13 @@ describe('AngularFirestoreCollection', () => {
it('should listen to all events for auditTrail() by default', async (done) => {
const ITEMS = 10;
let count = 0;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const sub = stocks.auditTrail().subscribe(data => {
count = count + 1;
- if(count === 1) {
- stocks.doc(names[0]).update({ price: 2});
+ if (count === 1) {
+ stocks.doc(names[0]).update({ price: 2 });
}
- if(count === 2) {
+ if (count === 2) {
sub.unsubscribe();
expect(data.length).toEqual(ITEMS + 1);
expect(data[data.length - 1].type).toEqual('modified');
@@ -445,7 +467,7 @@ describe('AngularFirestoreCollection', () => {
it('should be able to filter auditTrail() types - removed', async (done) => {
const ITEMS = 10;
- const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
+ const { ref, stocks, names } = await collectionHarness(afs, ITEMS);
const sub = stocks.auditTrail(['removed']).subscribe(data => {
sub.unsubscribe();
diff --git a/src/firestore/collection/collection.ts b/src/firestore/collection/collection.ts
index b816480ff..94a953577 100644
--- a/src/firestore/collection/collection.ts
+++ b/src/firestore/collection/collection.ts
@@ -1,15 +1,15 @@
-import { Observable, from } from 'rxjs';
+import { from, Observable } from 'rxjs';
import { fromCollectionRef } from '../observable/fromRef';
-import { map, filter, scan, observeOn } from 'rxjs/operators';
+import { filter, map, observeOn, scan } from 'rxjs/operators';
import { firestore } from 'firebase/app';
-import { DocumentChangeType, CollectionReference, Query, DocumentReference, DocumentData, DocumentChangeAction } from '../interfaces';
+import { CollectionReference, DocumentChangeAction, DocumentChangeType, DocumentData, DocumentReference, Query } from '../interfaces';
import { docChanges, sortedChanges } from './changes';
import { AngularFirestoreDocument } from '../document/document';
import { AngularFirestore } from '../firestore';
export function validateEventsArray(events?: DocumentChangeType[]) {
- if(!events || events!.length === 0) {
+ if (!events || events.length === 0) {
events = ['added', 'removed', 'modified'];
}
return events;
@@ -38,7 +38,7 @@ export function validateEventsArray(events?: DocumentChangeType[]) {
* // Subscribe to changes as snapshots. This provides you data updates as well as delta updates.
* fakeStock.valueChanges().subscribe(value => console.log(value));
*/
-export class AngularFirestoreCollection {
+export class AngularFirestoreCollection {
/**
* The constructor takes in a CollectionReference and Query to provide wrapper methods
* for data operations and data streaming.
@@ -47,7 +47,6 @@ export class AngularFirestoreCollection {
* when you update data it is not updating data to the window of your query unless
* the data fits the criteria of the query. See the AssociatedRefence type for details
* on this implication.
- * @param ref
*/
constructor(
public readonly ref: CollectionReference,
@@ -58,10 +57,9 @@ export class AngularFirestoreCollection {
* Listen to the latest change in the stream. This method returns changes
* as they occur and they are not sorted by query order. This allows you to construct
* your own data structure.
- * @param events
*/
stateChanges(events?: DocumentChangeType[]): Observable[]> {
- if(!events || events.length === 0) {
+ if (!events || events.length === 0) {
return docChanges(this.query, this.afs.schedulers.outsideAngular).pipe(
this.afs.keepUnstableUntilFirst
);
@@ -76,7 +74,6 @@ export class AngularFirestoreCollection {
/**
* Create a stream of changes as they occur it time. This method is similar to stateChanges()
* but it collects each event in an array over time.
- * @param events
*/
auditTrail(events?: DocumentChangeType[]): Observable[]> {
return this.stateChanges(events).pipe(scan((current, action) => [...current, ...action], []));
@@ -85,7 +82,6 @@ export class AngularFirestoreCollection {
/**
* Create a stream of synchronized changes. This method keeps the local array in sorted
* query order.
- * @param events
*/
snapshotChanges(events?: DocumentChangeType[]): Observable[]> {
const validatedEvents = validateEventsArray(events);
@@ -100,22 +96,22 @@ export class AngularFirestoreCollection {
*
* If the `idField` option is provided, document IDs are included and mapped to the
* provided `idField` property name.
- * @param options
*/
- valueChanges(): Observable
- valueChanges({}): Observable
- valueChanges(options: {idField: K}): Observable<(T & { [T in K]: string })[]>
+ valueChanges(): Observable;
+ // tslint:disable-next-line:unified-signatures
+ valueChanges({}): Observable;
+ valueChanges(options: {idField: K}): Observable<(T & { [T in K]: string })[]>;
valueChanges(options: {idField?: K} = {}): Observable {
return fromCollectionRef(this.query, this.afs.schedulers.outsideAngular)
.pipe(
map(actions => actions.payload.docs.map(a => {
if (options.idField) {
return {
- ...a.data() as Object,
+ ...a.data() as {},
...{ [options.idField]: a.id }
} as T & { [T in K]: string };
} else {
- return a.data()
+ return a.data();
}
})),
this.afs.keepUnstableUntilFirst
@@ -124,7 +120,6 @@ export class AngularFirestoreCollection {
/**
* Retrieve the results of the query once.
- * @param options
*/
get(options?: firestore.GetOptions) {
return from(this.query.get(options)).pipe(
@@ -145,7 +140,6 @@ export class AngularFirestoreCollection {
/**
* Create a reference to a single document in a collection.
- * @param path
*/
doc(path?: string): AngularFirestoreDocument {
return new AngularFirestoreDocument(this.ref.doc(path), this.afs);
diff --git a/src/firestore/document/document.spec.ts b/src/firestore/document/document.spec.ts
index b05eb516a..a6acc0b52 100644
--- a/src/firestore/document/document.spec.ts
+++ b/src/firestore/document/document.spec.ts
@@ -1,20 +1,18 @@
-import { FirebaseApp, AngularFireModule } from '@angular/fire';
+import { AngularFireModule, FirebaseApp } from '@angular/fire';
import { AngularFirestore, SETTINGS } from '../firestore';
import { AngularFirestoreModule } from '../firestore.module';
-import { AngularFirestoreDocument } from '../document/document';
-import { Observable, Subscription } from 'rxjs';
+import { AngularFirestoreDocument } from './document';
import { take } from 'rxjs/operators';
-import { TestBed, inject } from '@angular/core/testing';
+import { TestBed } from '@angular/core/testing';
import { COMMON_CONFIG } from '../../test-config';
-import { Stock, randomName, FAKE_STOCK_DATA, rando } from '../utils.spec';
+import { FAKE_STOCK_DATA, rando, randomName, Stock } from '../utils.spec';
import 'firebase/firestore';
describe('AngularFirestoreDocument', () => {
let app: FirebaseApp;
let afs: AngularFirestore;
- let sub: Subscription;
beforeEach(() => {
TestBed.configureTestingModule({
@@ -26,10 +24,9 @@ describe('AngularFirestoreDocument', () => {
{ provide: SETTINGS, useValue: { host: 'localhost:8080', ssl: false } }
]
});
- inject([FirebaseApp, AngularFirestore], (_app: FirebaseApp, _afs: AngularFirestore) => {
- app = _app;
- afs = _afs;
- })();
+
+ app = TestBed.inject(FirebaseApp);
+ afs = TestBed.inject(AngularFirestore);
});
afterEach(() => {
@@ -59,7 +56,7 @@ describe('AngularFirestoreDocument', () => {
await stock.set(FAKE_STOCK_DATA);
const obs$ = stock.valueChanges();
obs$.pipe(take(1)).subscribe(async data => {
- expect(JSON.stringify(data)).toBe(JSON.stringify(FAKE_STOCK_DATA));
+ expect(data).toEqual(FAKE_STOCK_DATA);
stock.delete().then(done).catch(done.fail);
});
});
diff --git a/src/firestore/document/document.ts b/src/firestore/document/document.ts
index 62c854936..8592c5a5f 100644
--- a/src/firestore/document/document.ts
+++ b/src/firestore/document/document.ts
@@ -1,5 +1,5 @@
-import { Observable, from } from 'rxjs';
-import { DocumentReference, SetOptions, DocumentData, QueryFn, Action, DocumentSnapshot } from '../interfaces';
+import { from, Observable } from 'rxjs';
+import { Action, DocumentData, DocumentReference, DocumentSnapshot, QueryFn, SetOptions } from '../interfaces';
import { fromDocRef } from '../observable/fromRef';
import { map, observeOn } from 'rxjs/operators';
import { AngularFirestore, associateQuery } from '../firestore';
@@ -28,19 +28,16 @@ import { firestore } from 'firebase/app';
* // OR! Transform using Observable.from() and the data is unwrapped for you
* Observable.from(fakeStock).subscribe(value => console.log(value));
*/
-export class AngularFirestoreDocument {
+export class AngularFirestoreDocument {
/**
* The contstuctor takes in a DocumentReference to provide wrapper methods
* for data operations, data streaming, and Symbol.observable.
- * @param ref
*/
constructor(public ref: DocumentReference, private afs: AngularFirestore) { }
/**
* Create or overwrite a single document.
- * @param data
- * @param options
*/
set(data: T, options?: SetOptions): Promise {
return this.ref.set(data, options);
@@ -48,7 +45,6 @@ export class AngularFirestoreDocument {
/**
* Update some fields of a document without overwriting the entire document.
- * @param data
*/
update(data: Partial): Promise {
return this.ref.update(data);
@@ -64,10 +60,8 @@ export class AngularFirestoreDocument {
/**
* Create a reference to a sub-collection given a path and an optional query
* function.
- * @param path
- * @param queryFn
*/
- collection(path: string, queryFn?: QueryFn): AngularFirestoreCollection {
+ collection(path: string, queryFn?: QueryFn): AngularFirestoreCollection {
const collectionRef = this.ref.collection(path);
const { ref, query } = associateQuery(collectionRef, queryFn);
return new AngularFirestoreCollection