Skip to content

Adding AngularFireAnalytics, AngularFireRemoteConfig, and refactoring DI Tokens #2187

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 41 commits into from
Jan 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
e5be927
Firebase v7, analytics and remote-config
jamesdaniels Oct 2, 2019
1cf961b
Cleaning up the DI tokens
jamesdaniels Oct 2, 2019
f5f67ad
Cleaning things up
jamesdaniels Oct 4, 2019
73413e8
DI and jazz
jamesdaniels Oct 4, 2019
dd0efb1
Bumping the tests
jamesdaniels Nov 8, 2019
dffca53
Adding to the root-spec
jamesdaniels Nov 9, 2019
9731e3c
Spelling is good.
jamesdaniels Nov 9, 2019
7308f9c
Merge branch 'master' into firebase-v7
jamesdaniels Nov 9, 2019
dcdf8bc
Have to include UMDs in the karma.conf
jamesdaniels Nov 9, 2019
6f71d46
Just importing performance is destablizing the app
jamesdaniels Nov 11, 2019
6638b9d
Merge branch 'master' into firebase-v7
jamesdaniels Nov 12, 2019
d7d52c8
Adding the zone arg to the app factory
jamesdaniels Nov 12, 2019
eb4bc00
First pass on the new RC API and the start of the AngularFireLazy effort
jamesdaniels Nov 13, 2019
89344cc
Update src/remote-config/tsconfig-test.json
jamesdaniels Nov 14, 2019
075afe6
Reworking things a bit
jamesdaniels Nov 20, 2019
b8b351a
Router as optional, drop this/private from screen tracking
jamesdaniels Nov 20, 2019
1e39052
Minor changes to RC
jamesdaniels Nov 20, 2019
768c21b
It's firebase_screen_class
jamesdaniels Nov 21, 2019
60c0cad
Reworking analytics
jamesdaniels Nov 22, 2019
9b2e920
current!
jamesdaniels Nov 22, 2019
916e069
Use _loadedConfig if available and scope screen_id on outlet
jamesdaniels Nov 22, 2019
5955925
Fixing the types to handle older Firebase SDKs
jamesdaniels Nov 22, 2019
659165e
SEMVER notes on the DI tokens
jamesdaniels Nov 22, 2019
62d90b9
Starting on the docs
jamesdaniels Nov 22, 2019
1a43ad1
Merge branch 'firebase-v7' of github.com:angular/angularfire2 into fi…
jamesdaniels Nov 22, 2019
67e1b55
Monitoring...
jamesdaniels Dec 9, 2019
91778ff
More work on analytics
jamesdaniels Dec 11, 2019
460170c
more expirimentation
jamesdaniels Dec 12, 2019
3c1ad1f
Flushing out RC more and fixing SSR issues
jamesdaniels Dec 14, 2019
e2d83c8
New RC API
jamesdaniels Dec 16, 2019
4601932
Mapping to objects and templates, budget pipe
jamesdaniels Dec 17, 2019
7fe92ed
More strict types
jamesdaniels Dec 17, 2019
d47dc3f
Fix proxy in Node, get component name for analytics in both JIT and AOT
jamesdaniels Dec 21, 2019
05c840b
Cleaning things up, beyond docs ready for release
jamesdaniels Jan 7, 2020
b46b382
Docs and cleanup
jamesdaniels Jan 7, 2020
ff65db8
Fixing analytics spec
jamesdaniels Jan 7, 2020
baaeccf
Adding more API to the docs
jamesdaniels Jan 7, 2020
04a3bb1
Further simplifications to the DI tokens
jamesdaniels Jan 7, 2020
c37fcb7
Add Analytics and RemoteConfig to install-and-setup
jamesdaniels Jan 7, 2020
6753a67
Merge branch 'master' into firebase-v7
jamesdaniels Jan 7, 2020
6f114c6
Our RC Value implements a Partial, so minors dont break
jamesdaniels Jan 7, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ export class MyApp {

- [Installation & Setup](docs/install-and-setup.md)

### **NEW:** Monitor usage of your application in production

> `AngularFireAnalytics` provides a convient method of interacting with Google Analytics in your Angular application. The provided `ScreenTrackingService` and `UserTrackingService` automatically log events when you're using the Angular Router or Firebase Authentication respectively. [Learn more about Google Analytics](https://firebase.google.com/docs/analytics).

- [Getting started with Google Analytics](docs/analytics/getting-started.md)

### Interacting with your database(s)

Firebase offers two cloud-based, client-accessible database solutions that support realtime data syncing. [Learn about the differences between them in the Firebase Documentation](https://firebase.google.com/docs/firestore/rtdb-vs-firestore).
Expand Down Expand Up @@ -94,11 +100,19 @@ Firebase offers two cloud-based, client-accessible database solutions that suppo

- [Getting started with Cloud Storage](docs/storage/storage.md)

### Send push notifications
### Receive push notifications

- [Getting started with Firebase Messaging](docs/messaging/messaging.md)

### Monitor your application performance in production
### **BETA:** Change behavior and appearance of your application without deploying

> Firebase Remote Config is a cloud service that lets you change the behavior and appearance of your app without requiring users to download an app update. [Learn more about Remote Config](https://firebase.google.com/docs/remote-config).

- [Getting started with Remote Config](docs/remote-config/getting-started.md)

### **NEW:** Monitor your application performance in production

> Firebase Performance Monitoring is a service that helps you to gain insight into the performance characteristics of your iOS, Android, and web apps. [Learn more about Performance Monitoring](https://firebase.google.com/docs/perf-mon).

- [Getting started with Performance Monitoring](docs/performance/getting-started.md)

Expand Down
135 changes: 135 additions & 0 deletions docs/analytics/getting-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# Getting started with Google Analytics

`AngularFireAnalytics` dynamically imports the `firebase/analytics` library and provides a promisified version of the [Firebase Analytics SDK (`firebase.analytics.Analytics`)](https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics.html).

### API:

```ts
class AngularFireAnalytics {
updateConfig(options: {[key:string]: any}): Promise<void>;

// from firebase.analytics() proxy:
logEvent(eventName: string, eventParams?: {[key: string]: any}, options?: analytics.AnalyticsCallOptions): Promise<void>;
setCurrentScreen(screenName: string, options?: analytics.AnalyticsCallOptions): Promise<void>;
setUserId(id: string, options?: analytics.AnalyticsCallOptions): Promise<void>;
setUserProperties(properties: analytics.CustomParams, options?: analytics.AnalyticsCallOptions): Promise<void>;
setAnalyticsCollectionEnabled(enabled: boolean): Promise<void>;
app: Promise<app.App>;
}

COLLECTION_ENABLED = InjectionToken<boolean>;
APP_VERSION = InjectionToken<string>;
APP_NAME = InjectionToken<string>;
DEBUG_MODE = InjectionToken<boolean>;
CONFIG = InjectionToken<Config>;
```

### Usage:

```ts
import { AngularFireAnalyticsModule } from '@angular/fire/analytics';

@NgModule({
imports: [
AngularFireModule.initializeApp(environment.firebase),
AngularFireAnalyticsModule
]
})
export class AppModule { }
```

`AngularFireAnalyticsModule` will dyanamically import and configure `firebase/analytics`. A `page_view` event will automatically be logged (see `CONFIG` below if you wish to disable this behavior.)

In your component you can then dependency inject `AngularFireAnalytics` and make calls against the SDK:

```ts
import { AngularFireAnalytics } from '@angular/fire/analytics';

constructor(analytics: AngularFireAnalytics) {
analytics.logEvent('custom_event', { ... });
}
```

## Tracking Screen Views

You can log [`screen_view` events](https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics.html#parameters_10) yourself of course, but AngularFire provides the `ScreenTrackingService` which automatically integrates with the Angular Router to provide Firebase with screen view tracking. You simply can integrate like so:

```ts
import { AngularFireAnalyticsModule, ScreenTrackingService } from '@angular/fire/analytics';

@NgModule({
imports: [
AngularFireModule.initializeApp(environment.firebase),
AngularFireAnalyticsModule
],
providers: [
ScreenTrackingService
]
})
export class AppModule { }
```

`AngularFireAnalyticsModule` will initialize `ScreenTrackingService` if it is provided.

## Tracking User Identifiers

To enrich your Analytics data you can track the currently signed in user by setting [`setuserid`](https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics.html#setuserid) and [`setUserProperties`](https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics.html#set-user-properties). AngularFire provides a `UserTrackingService` which will dynamically import `firebase/auth`, monitor for changes in the logged in user, and call `setuserid` for you automatically.


```ts
import { AngularFireAnalyticsModule, UserTrackingService } from '@angular/fire/analytics';

@NgModule({
imports: [
AngularFireModule.initializeApp(environment.firebase),
AngularFireAnalyticsModule
],
providers: [
UserTrackingService
]
})
export class AppModule { }
```

`AngularFireAnalyticsModule` will initialize `UserTrackingService` if it is provided.

## Configuration with Dependency Injection

### Configure Google Analtyics with `CONFIG`

Using the `CONFIG` DI Token (*default: {}*) will allow you to configure Google Analytics. E.g, you could skip sending the initial `page_view` event, anonymize IP addresses, and disallow ads personalization signals for all events like so:

```ts
import { AngularFireAnalyticsModule, CONFIG } from '@angular/fire/analytics';

@NgModule({
imports: [
AngularFireModule.initializeApp(environment.firebase),
AngularFireAnalyticsModule
],
providers: [
{ provide: CONFIG, useValue: {
send_page_view: false,
allow_ad_personalization_signals: false,
anonymize_ip: true
} }
]
})
export class AppModule { }
```

See the gtag.js documentation to learn of the different configuration options at your disposal.

### Use DebugView `DEBUG_MODE`

To use [DebugView in Analtyics](https://console.firebase.google.com/project/_/analytics/debugview) set `DEBUG_MODE` to `true` (*default: false*).

### Track deployments with `APP_NAME` and `APP_VERSION`

If you provide `APP_NAME` and `APP_VERSION` (*default: undefined*) you will be able to [track version adoption](https://console.firebase.google.com/project/_/analytics/latestrelease) of your PWA.

### Disable analytics collection via `COLLECTION_ENABLED`

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.
4 changes: 4 additions & 0 deletions docs/install-and-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,12 @@ export class AppModule {}

After adding the AngularFireModule you also need to add modules for the individual @NgModules that your application needs.

- `AngularFireAnalytics`
- `AngularFireAuthModule`
- `AngularFireDatabaseModule`
- `AngularFireFunctionsModule`
- `AngularFirestoreModule`
- `AngularFireRemoteConfigModule`
- `AngularFireStorageModule`
- `AngularFireMessagingModule`
- `AngularFirePerformanceModule`
Expand All @@ -113,6 +115,7 @@ import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { AngularFireModule } from '@angular/fire';
import { AngularFireAnalyticsModule } from '@angular/fire/analytics';
import { AngularFirestoreModule } from '@angular/fire/firestore';
import { AngularFireStorageModule } from '@angular/fire/storage';
import { AngularFireAuthModule } from '@angular/fire/auth';
Expand All @@ -122,6 +125,7 @@ import { environment } from '../environments/environment';
imports: [
BrowserModule,
AngularFireModule.initializeApp(environment.firebase, 'my-app-name'), // imports firebase/app needed for everything
AngularFireAnalyticsModule, // dynamically imports firebase/analytics
AngularFirestoreModule, // imports firebase/firestore, only needed for database features
AngularFireAuthModule, // imports firebase/auth, only needed for auth features,
AngularFireStorageModule // imports firebase/storage only needed for storage features
Expand Down
128 changes: 128 additions & 0 deletions docs/remote-config/getting-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<h1>Getting started with Remote Config <em><abbr title="beta">β<abbr></em></h1>

`AngularFireRemoteConfig` dynamically imports the `firebase/remote-config` library on demand, provides convenience observables, pipes, and a promisified version of the [Firebase Remote Config SDK (`firebase.remoteConfig.RemoteConfig`)](https://firebase.google.com/docs/reference/js/firebase.remoteconfig.RemoteConfig).

### API:

```ts
class AngularFireRemoteConfigModule { }

interface ConfigTemplate {[key:string]: string|number|boolean}

type Parameter extends remoteConfig.Value {
key: string,
fetchTimeMillis: number
}

class AngularFireRemoteConfig {
changes: Observable<Parameter>;
parameters: Observable<Parameter[]>;
numbers: Observable<{[key:string]: number|undefined}> & {[key:string]: Observable<number>};
booleans: Observable<{[key:string]: boolean|undefined}> & {[key:string]: Observable<boolean>};
strings: Observable<{[key:string]: string|undefined}> & {[key:string]: Observable<string|undefined>};

// from firebase.remoteConfig() proxy:
activate: () => Promise<boolean>;
ensureInitialized: () => Promise<void>;
fetch: () => Promise<void>;
fetchAndActivate: () => Promise<boolean>;
getAll: () => Promise<{[key:string]: remoteConfig.Value}>;
getBoolean: (key:string) => Promise<boolean>;
getNumber: (key:string) => Promise<number>;
getString: (key:string) => Promise<string>;
getValue: (key:string) => Promise<remoteConfig.Value>;
setLogLevel: (logLevel: remoteConfig.LogLevel) => Promise<void>;
settings: Promise<remoteConfig.Settings>;
defaultConfig: Promise<{[key: string]: string | number | boolean}>;
fetchTimeMillis: Promise<number>;
lastFetchStatus: Promise<remoteConfig.FetchStatus>;
}

// Pipes for working with .changes and .parameters
filterRemote: () => MonoTypeOperatorFunction<Parameter | Parameter[]>
filterFresh: (interval: number) => MonoTypeOperatorFunction<Parameter | Parameter[]>
budget: <T>(interval: number) => MonoTypeOperatorFunction<T>

// scanToObject is for use with .changes
scanToObject: () => OperatorFunction<Parameter, {[key: string]: string|undefined}>

// mapToObject is the same behavior are scanToObject but for use with .parameters,
mapToObject: () => OperatorFunction<Parameter[], {[key: string]: string|undefined}>

SETTINGS = InjectionToken<remoteConfig.Settings>;
DEFAULTS = InjectionToken<ConfigTemplate>;
```

## Configuration with Dependency Injection

### Configure Remote Config with `SETTINGS`

Using the `SETTINGS` DI Token (*default: {}*) will allow you to [configure Firebase Remote Config](https://firebase.google.com/docs/reference/js/firebase.remoteconfig.Settings.html).

### Configure default values with `DEFAULTS`

Providing `DEFAULTS ({[key: string]: string | number | boolean})` has `AngularFireRemoteConfig` emit the provided defaults first, which allows you to count on Remote Config when the user is offline or in environments that the Remote Config service does not handle (i.e, Server Side Rendering).

## Putting it all together:

```ts
@NgModule({
imports: [
AngularFireModule.initializeApp(environment.firebase),
AngularFireRemoteConfigModule
],
providers: [
{ provide: DEFAULT_CONFIG, useValue: { enableAwesome: true } },
{
provide: REMOTE_CONFIG_SETTINGS,
useFactory: () => isDevMode() ? { minimumFetchIntervalMillis: 10_000 } : {}
}
]
})
export class AppModule { }

...

constructor(remoteConfig: AngularFireRemoteConfig) {
remoteConfig.changes.pipe(
filterFresh(172_800_000), // ensure we have values from at least 48 hours ago
first(),
// scanToObject when used this way is similar to defaults
// but most importantly smart-casts remote config values and adds type safety
scanToObject({
enableAwesome: true,
titleBackgroundColor: 'blue',
titleFontSize: 12
})
).subscribe(…);

// all remote config values cast as strings
remoteConfig.strings.subscribe(...)
remoteConfig.booleans.subscribe(...); // as booleans
remoteConfig.numbers.subscribe(...); // as numbers

// convenience for observing a single string
remoteConfig.strings.titleBackgroundColor.subscribe(...);
remoteConfig.booleans.enableAwesome.subscribe(...); // boolean
remoteConfig.numbers.titleBackgroundColor.subscribe(...); // number

// however those may emit more than once as the remote config cache fires and gets fresh values from the server
// you can filter it out of .changes for more control:
remoteConfig.changes.pipe(
filter(param => param.key === 'titleBackgroundColor'),
map(param => param.asString())
// budget at most 800ms and return the freshest value possible in that time
// our budget pipe is similar to timeout but won't error or abort the pending server fetch (it won't emit it, if the deadline is exceeded, but it will have been fetched so can use the freshest values on next subscription)
budget(800),
last()
).subscribe(...)

// just like .changes, but scanned as into an array
remoteConfig.parameters.subscribe(all => ...);

// or make promisified firebase().remoteConfig() calls direct off AngularFireRemoteConfig
// using our proxy
remoteConfig.getAll().then(all => ...);
remoteConfig.lastFetchStatus.then(status => ...);
}
```
3 changes: 3 additions & 0 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,13 @@ module.exports = function(config) {
'node_modules/firebase/firebase-storage.js',
'dist/packages-dist/bundles/core.umd.{js,map}',
'dist/packages-dist/bundles/auth.umd.{js,map}',
'dist/packages-dist/bundles/analytics.umd.{js,map}',
'dist/packages-dist/bundles/auth-guard.umd.{js,map}',
'dist/packages-dist/bundles/database.umd.{js,map}',
'dist/packages-dist/bundles/firestore.umd.{js,map}',
'dist/packages-dist/bundles/functions.umd.{js,map}',
'dist/packages-dist/bundles/messaging.umd.{js,map}',
'dist/packages-dist/bundles/remote-config.umd.{js,map}',
'dist/packages-dist/bundles/storage.umd.{js,map}',
'dist/packages-dist/bundles/performance.umd.{js,map}',
'dist/packages-dist/bundles/database-deprecated.umd.{js,map}',
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@angular/fire",
"version": "5.2.3",
"version": "5.3.0",
"description": "The official library of Firebase and Angular.",
"private": true,
"scripts": {
Expand Down
17 changes: 17 additions & 0 deletions src/analytics/analytics.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { NgModule, Optional } from '@angular/core';
import { UserTrackingService, ScreenTrackingService } from './analytics.service';
import { AngularFireAnalytics } from './analytics';

@NgModule({
providers: [ AngularFireAnalytics ]
})
export class AngularFireAnalyticsModule {
constructor(
analytics: AngularFireAnalytics,
@Optional() screenTracking: ScreenTrackingService,
@Optional() userTracking: UserTrackingService
) {
// calling anything on analytics will eagerly load the SDK
analytics.app;
}
}
Loading