Skip to content

Commit 0c5a157

Browse files
authored
Adding AngularFireAnalytics, AngularFireRemoteConfig, and refactoring DI Tokens (#2187)
1 parent 1c89c38 commit 0c5a157

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1423
-52
lines changed

README.md

+16-2
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ export class MyApp {
6464

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

67+
### **NEW:** Monitor usage of your application in production
68+
69+
> `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).
70+
71+
- [Getting started with Google Analytics](docs/analytics/getting-started.md)
72+
6773
### Interacting with your database(s)
6874

6975
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).
@@ -94,11 +100,19 @@ Firebase offers two cloud-based, client-accessible database solutions that suppo
94100

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

97-
### Send push notifications
103+
### Receive push notifications
98104

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

101-
### Monitor your application performance in production
107+
### **BETA:** Change behavior and appearance of your application without deploying
108+
109+
> 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).
110+
111+
- [Getting started with Remote Config](docs/remote-config/getting-started.md)
112+
113+
### **NEW:** Monitor your application performance in production
114+
115+
> 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).
102116
103117
- [Getting started with Performance Monitoring](docs/performance/getting-started.md)
104118

docs/analytics/getting-started.md

+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# Getting started with Google Analytics
2+
3+
`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).
4+
5+
### API:
6+
7+
```ts
8+
class AngularFireAnalytics {
9+
updateConfig(options: {[key:string]: any}): Promise<void>;
10+
11+
// from firebase.analytics() proxy:
12+
logEvent(eventName: string, eventParams?: {[key: string]: any}, options?: analytics.AnalyticsCallOptions): Promise<void>;
13+
setCurrentScreen(screenName: string, options?: analytics.AnalyticsCallOptions): Promise<void>;
14+
setUserId(id: string, options?: analytics.AnalyticsCallOptions): Promise<void>;
15+
setUserProperties(properties: analytics.CustomParams, options?: analytics.AnalyticsCallOptions): Promise<void>;
16+
setAnalyticsCollectionEnabled(enabled: boolean): Promise<void>;
17+
app: Promise<app.App>;
18+
}
19+
20+
COLLECTION_ENABLED = InjectionToken<boolean>;
21+
APP_VERSION = InjectionToken<string>;
22+
APP_NAME = InjectionToken<string>;
23+
DEBUG_MODE = InjectionToken<boolean>;
24+
CONFIG = InjectionToken<Config>;
25+
```
26+
27+
### Usage:
28+
29+
```ts
30+
import { AngularFireAnalyticsModule } from '@angular/fire/analytics';
31+
32+
@NgModule({
33+
imports: [
34+
AngularFireModule.initializeApp(environment.firebase),
35+
AngularFireAnalyticsModule
36+
]
37+
})
38+
export class AppModule { }
39+
```
40+
41+
`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.)
42+
43+
In your component you can then dependency inject `AngularFireAnalytics` and make calls against the SDK:
44+
45+
```ts
46+
import { AngularFireAnalytics } from '@angular/fire/analytics';
47+
48+
constructor(analytics: AngularFireAnalytics) {
49+
analytics.logEvent('custom_event', { ... });
50+
}
51+
```
52+
53+
## Tracking Screen Views
54+
55+
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:
56+
57+
```ts
58+
import { AngularFireAnalyticsModule, ScreenTrackingService } from '@angular/fire/analytics';
59+
60+
@NgModule({
61+
imports: [
62+
AngularFireModule.initializeApp(environment.firebase),
63+
AngularFireAnalyticsModule
64+
],
65+
providers: [
66+
ScreenTrackingService
67+
]
68+
})
69+
export class AppModule { }
70+
```
71+
72+
`AngularFireAnalyticsModule` will initialize `ScreenTrackingService` if it is provided.
73+
74+
## Tracking User Identifiers
75+
76+
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.
77+
78+
79+
```ts
80+
import { AngularFireAnalyticsModule, UserTrackingService } from '@angular/fire/analytics';
81+
82+
@NgModule({
83+
imports: [
84+
AngularFireModule.initializeApp(environment.firebase),
85+
AngularFireAnalyticsModule
86+
],
87+
providers: [
88+
UserTrackingService
89+
]
90+
})
91+
export class AppModule { }
92+
```
93+
94+
`AngularFireAnalyticsModule` will initialize `UserTrackingService` if it is provided.
95+
96+
## Configuration with Dependency Injection
97+
98+
### Configure Google Analtyics with `CONFIG`
99+
100+
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:
101+
102+
```ts
103+
import { AngularFireAnalyticsModule, CONFIG } from '@angular/fire/analytics';
104+
105+
@NgModule({
106+
imports: [
107+
AngularFireModule.initializeApp(environment.firebase),
108+
AngularFireAnalyticsModule
109+
],
110+
providers: [
111+
{ provide: CONFIG, useValue: {
112+
send_page_view: false,
113+
allow_ad_personalization_signals: false,
114+
anonymize_ip: true
115+
} }
116+
]
117+
})
118+
export class AppModule { }
119+
```
120+
121+
See the gtag.js documentation to learn of the different configuration options at your disposal.
122+
123+
### Use DebugView `DEBUG_MODE`
124+
125+
To use [DebugView in Analtyics](https://console.firebase.google.com/project/_/analytics/debugview) set `DEBUG_MODE` to `true` (*default: false*).
126+
127+
### Track deployments with `APP_NAME` and `APP_VERSION`
128+
129+
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.
130+
131+
### Disable analytics collection via `COLLECTION_ENABLED`
132+
133+
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)`.
134+
135+
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.

docs/install-and-setup.md

+4
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,12 @@ export class AppModule {}
9696

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

99+
- `AngularFireAnalytics`
99100
- `AngularFireAuthModule`
100101
- `AngularFireDatabaseModule`
101102
- `AngularFireFunctionsModule`
102103
- `AngularFirestoreModule`
104+
- `AngularFireRemoteConfigModule`
103105
- `AngularFireStorageModule`
104106
- `AngularFireMessagingModule`
105107
- `AngularFirePerformanceModule`
@@ -113,6 +115,7 @@ import { BrowserModule } from '@angular/platform-browser';
113115
import { NgModule } from '@angular/core';
114116
import { AppComponent } from './app.component';
115117
import { AngularFireModule } from '@angular/fire';
118+
import { AngularFireAnalyticsModule } from '@angular/fire/analytics';
116119
import { AngularFirestoreModule } from '@angular/fire/firestore';
117120
import { AngularFireStorageModule } from '@angular/fire/storage';
118121
import { AngularFireAuthModule } from '@angular/fire/auth';
@@ -122,6 +125,7 @@ import { environment } from '../environments/environment';
122125
imports: [
123126
BrowserModule,
124127
AngularFireModule.initializeApp(environment.firebase, 'my-app-name'), // imports firebase/app needed for everything
128+
AngularFireAnalyticsModule, // dynamically imports firebase/analytics
125129
AngularFirestoreModule, // imports firebase/firestore, only needed for database features
126130
AngularFireAuthModule, // imports firebase/auth, only needed for auth features,
127131
AngularFireStorageModule // imports firebase/storage only needed for storage features

docs/remote-config/getting-started.md

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
<h1>Getting started with Remote Config <em><abbr title="beta">β<abbr></em></h1>
2+
3+
`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).
4+
5+
### API:
6+
7+
```ts
8+
class AngularFireRemoteConfigModule { }
9+
10+
interface ConfigTemplate {[key:string]: string|number|boolean}
11+
12+
type Parameter extends remoteConfig.Value {
13+
key: string,
14+
fetchTimeMillis: number
15+
}
16+
17+
class AngularFireRemoteConfig {
18+
changes: Observable<Parameter>;
19+
parameters: Observable<Parameter[]>;
20+
numbers: Observable<{[key:string]: number|undefined}> & {[key:string]: Observable<number>};
21+
booleans: Observable<{[key:string]: boolean|undefined}> & {[key:string]: Observable<boolean>};
22+
strings: Observable<{[key:string]: string|undefined}> & {[key:string]: Observable<string|undefined>};
23+
24+
// from firebase.remoteConfig() proxy:
25+
activate: () => Promise<boolean>;
26+
ensureInitialized: () => Promise<void>;
27+
fetch: () => Promise<void>;
28+
fetchAndActivate: () => Promise<boolean>;
29+
getAll: () => Promise<{[key:string]: remoteConfig.Value}>;
30+
getBoolean: (key:string) => Promise<boolean>;
31+
getNumber: (key:string) => Promise<number>;
32+
getString: (key:string) => Promise<string>;
33+
getValue: (key:string) => Promise<remoteConfig.Value>;
34+
setLogLevel: (logLevel: remoteConfig.LogLevel) => Promise<void>;
35+
settings: Promise<remoteConfig.Settings>;
36+
defaultConfig: Promise<{[key: string]: string | number | boolean}>;
37+
fetchTimeMillis: Promise<number>;
38+
lastFetchStatus: Promise<remoteConfig.FetchStatus>;
39+
}
40+
41+
// Pipes for working with .changes and .parameters
42+
filterRemote: () => MonoTypeOperatorFunction<Parameter | Parameter[]>
43+
filterFresh: (interval: number) => MonoTypeOperatorFunction<Parameter | Parameter[]>
44+
budget: <T>(interval: number) => MonoTypeOperatorFunction<T>
45+
46+
// scanToObject is for use with .changes
47+
scanToObject: () => OperatorFunction<Parameter, {[key: string]: string|undefined}>
48+
49+
// mapToObject is the same behavior are scanToObject but for use with .parameters,
50+
mapToObject: () => OperatorFunction<Parameter[], {[key: string]: string|undefined}>
51+
52+
SETTINGS = InjectionToken<remoteConfig.Settings>;
53+
DEFAULTS = InjectionToken<ConfigTemplate>;
54+
```
55+
56+
## Configuration with Dependency Injection
57+
58+
### Configure Remote Config with `SETTINGS`
59+
60+
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).
61+
62+
### Configure default values with `DEFAULTS`
63+
64+
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).
65+
66+
## Putting it all together:
67+
68+
```ts
69+
@NgModule({
70+
imports: [
71+
AngularFireModule.initializeApp(environment.firebase),
72+
AngularFireRemoteConfigModule
73+
],
74+
providers: [
75+
{ provide: DEFAULT_CONFIG, useValue: { enableAwesome: true } },
76+
{
77+
provide: REMOTE_CONFIG_SETTINGS,
78+
useFactory: () => isDevMode() ? { minimumFetchIntervalMillis: 10_000 } : {}
79+
}
80+
]
81+
})
82+
export class AppModule { }
83+
84+
...
85+
86+
constructor(remoteConfig: AngularFireRemoteConfig) {
87+
remoteConfig.changes.pipe(
88+
filterFresh(172_800_000), // ensure we have values from at least 48 hours ago
89+
first(),
90+
// scanToObject when used this way is similar to defaults
91+
// but most importantly smart-casts remote config values and adds type safety
92+
scanToObject({
93+
enableAwesome: true,
94+
titleBackgroundColor: 'blue',
95+
titleFontSize: 12
96+
})
97+
).subscribe(…);
98+
99+
// all remote config values cast as strings
100+
remoteConfig.strings.subscribe(...)
101+
remoteConfig.booleans.subscribe(...); // as booleans
102+
remoteConfig.numbers.subscribe(...); // as numbers
103+
104+
// convenience for observing a single string
105+
remoteConfig.strings.titleBackgroundColor.subscribe(...);
106+
remoteConfig.booleans.enableAwesome.subscribe(...); // boolean
107+
remoteConfig.numbers.titleBackgroundColor.subscribe(...); // number
108+
109+
// however those may emit more than once as the remote config cache fires and gets fresh values from the server
110+
// you can filter it out of .changes for more control:
111+
remoteConfig.changes.pipe(
112+
filter(param => param.key === 'titleBackgroundColor'),
113+
map(param => param.asString())
114+
// budget at most 800ms and return the freshest value possible in that time
115+
// 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)
116+
budget(800),
117+
last()
118+
).subscribe(...)
119+
120+
// just like .changes, but scanned as into an array
121+
remoteConfig.parameters.subscribe(all => ...);
122+
123+
// or make promisified firebase().remoteConfig() calls direct off AngularFireRemoteConfig
124+
// using our proxy
125+
remoteConfig.getAll().then(all => ...);
126+
remoteConfig.lastFetchStatus.then(status => ...);
127+
}
128+
```

karma.conf.js

+3
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,13 @@ module.exports = function(config) {
3434
'node_modules/firebase/firebase-storage.js',
3535
'dist/packages-dist/bundles/core.umd.{js,map}',
3636
'dist/packages-dist/bundles/auth.umd.{js,map}',
37+
'dist/packages-dist/bundles/analytics.umd.{js,map}',
3738
'dist/packages-dist/bundles/auth-guard.umd.{js,map}',
3839
'dist/packages-dist/bundles/database.umd.{js,map}',
3940
'dist/packages-dist/bundles/firestore.umd.{js,map}',
4041
'dist/packages-dist/bundles/functions.umd.{js,map}',
42+
'dist/packages-dist/bundles/messaging.umd.{js,map}',
43+
'dist/packages-dist/bundles/remote-config.umd.{js,map}',
4144
'dist/packages-dist/bundles/storage.umd.{js,map}',
4245
'dist/packages-dist/bundles/performance.umd.{js,map}',
4346
'dist/packages-dist/bundles/database-deprecated.umd.{js,map}',

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@angular/fire",
3-
"version": "5.2.3",
3+
"version": "5.3.0",
44
"description": "The official library of Firebase and Angular.",
55
"private": true,
66
"scripts": {

src/analytics/analytics.module.ts

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { NgModule, Optional } from '@angular/core';
2+
import { UserTrackingService, ScreenTrackingService } from './analytics.service';
3+
import { AngularFireAnalytics } from './analytics';
4+
5+
@NgModule({
6+
providers: [ AngularFireAnalytics ]
7+
})
8+
export class AngularFireAnalyticsModule {
9+
constructor(
10+
analytics: AngularFireAnalytics,
11+
@Optional() screenTracking: ScreenTrackingService,
12+
@Optional() userTracking: UserTrackingService
13+
) {
14+
// calling anything on analytics will eagerly load the SDK
15+
analytics.app;
16+
}
17+
}

0 commit comments

Comments
 (0)