How to add google analytics to jhipster production? - google-analytics

I followed this tutorial:
https://www.youtube.com/watch?v=3Bx8BU3wxaA&t=378s
When I run npm start it seems to be working fine but when I build the production file or run ./gradlew then it does not work anymore. No errors or anything.
Any ideas why?
To reproduce just generate new jhipster app & modify 2 files:
Modified files:
\src\main\webapp\index.html
...
<!-- jhipster-needle-add-resources-to-root - JHipster will add new resources here -->
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-*******-1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments); }
gtag('js', new Date());
</script>
</head>
src\main\webapp\app\layouts\main\main.component.ts
import { *** } from '***';
declare let gtag: any;
...
ngOnInit(): void {
this.router.events.subscribe(event => {
if (event instanceof NavigationEnd) {
this.updateTitle();
gtag('config', 'UA-******-1', {'page_path':event.urlAfterRedirects}); <--
}
...

Related

Firebase Hosting Flutter Web App not clearing Cache of first deploy

We build a flutter web app and deployed it via firebase hosting. Unfortunately, we didn't configure any caching settings in our initial deploy.
Now we deployed a newer version of our website but people still get the old website shown form the first deploy. What we tried so far:
Adding version no. to our index.html:
<"script src="main.dart.js?version=1" type="application/javascript"></script>
Adding meta Data in our header in index.html:
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
In our firebase.json we added the following headers:
"headers": [
{
"source": "**",
"headers": [
{
"key": "Cache-Control",
"value": "max-age=10"
}
]
}
]
All of these attempts were without any luck. We think that the problem is that the newer version doesn't have those entries in the files. How can we force this to update to our newest version? We even consider opening a new firebase project if that might help.
Try this:
In your flutter app, in the index.html within the web folder, add a version number after the src="main.dart.js
So your new line will look like this:
<script src="main.dart.js?version=1" type="application/javascript"></script>
Then increment the version number to 2, etc before you do each build.
update june 2021: add this line in index.html as the last script in the body
I see Flutter now include this script by default in index.html if project is created recently which has serviceWorkerVersion element which updates the version when compiling:
<script>
var serviceWorkerVersion = null;
var scriptLoaded = false;
function loadMainDartJs() {
if (scriptLoaded) {
return;
}
scriptLoaded = true;
var scriptTag = document.createElement('script');
scriptTag.src = 'main.dart.js';
scriptTag.type = 'application/javascript';
document.body.append(scriptTag);
}
if ('serviceWorker' in navigator) {
// Service workers are supported. Use them.
window.addEventListener('load', function () {
// Wait for registration to finish before dropping the <script> tag.
// Otherwise, the browser will load the script multiple times,
// potentially different versions.
var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;
navigator.serviceWorker.register(serviceWorkerUrl)
.then((reg) => {
function waitForActivation(serviceWorker) {
serviceWorker.addEventListener('statechange', () => {
if (serviceWorker.state == 'activated') {
console.log('Installed new service worker.');
loadMainDartJs();
}
});
}
if (!reg.active && (reg.installing || reg.waiting)) {
// No active web worker and we have installed or are installing
// one for the first time. Simply wait for it to activate.
waitForActivation(reg.installing ?? reg.waiting);
} else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
// When the app updates the serviceWorkerVersion changes, so we
// need to ask the service worker to update.
console.log('New service worker available.');
reg.update();
waitForActivation(reg.installing);
} else {
// Existing service worker is still good.
console.log('Loading app from service worker.');
loadMainDartJs();
}
});
// If service worker doesn't succeed in a reasonable amount of time,
// fallback to plaint <script> tag.
setTimeout(() => {
if (!scriptLoaded) {
console.warn(
'Failed to load app from service worker. Falling back to plain <script> tag.',
);
loadMainDartJs();
}
}, 4000);
});
} else {
// Service workers not supported. Just drop the <script> tag.
loadMainDartJs();
}
If you do not want to use the server worker caching, you can provide --pwa-strategy=none. Otherwise, the app will update after the new version has been downloaded and the page revisited.
Example:
flutter build web --release --pwa-strategy=none
It will Generate a service worker with no body. This is useful for local testing or in cases where the service worker caching functionality is not desirable
By default, it is set to offline-first: Attempt to cache the application shell eagerly and then lazily cache all subsequent assets as they are loaded. When making a network request for an asset, the offline cache wil be preferred.
Reference:
https://github.com/flutter/flutter/issues/71333#issuecomment-736732314
flutter help build web
When you build flutter web then in directory build/web generated a version.json file.
just increase build_number and version in this file.

Google Analytics gtag logging wrong custom dimension

We are using Google Analytics gtag in JavaScript, but is having problems with the logged data.
We have a tracking code in a .js file we reference on every page. We then call it on 'load' of the page.
The problem is, if go from one URL to a different URL with different relative site URL, it logs the wrong URL in the Custom Dimension field.
For example,
URL: https://example.com/sites/mysite1
SiteCollectionURL is https://example.com/sites/mysite1
UserID is 1234
then click a link to another site:
URL: https://example.com/sites/anothersite
SiteCollectionURL is https://example.com/sites/mysite1
UserID is 1234
If I debug it and place a breakpoint, SiteCollectionURL has correct value of https://example.com/sites/anothersite. Even using Dev Tools -> Network, it passes the correct URL in the "https://www.google-analytics.com/collect" call under cd1 parameter.
Below is the code:
function LogGoogleAnalytics(userId) {
//Format 'UA-{Number}-1'
var trackingId = 'UA-XXXXXXXXX-1';
var gaTagScript = document.createElement("script");
gaTagScript.type = "text/javascript";
gaTagScript.src = 'https://www.googletagmanager.com/gtag/js?id=' + trackingId;
gaTagScript.async = true;
document.head.appendChild(gaTagScript);
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', trackingId, {
'custom_map': {
'dimension1': 'SiteCollectionURL',
'dimension2': 'UserID'
}
});
var siteUrl = getSiteCollectionURL();
gtag('event', 'pageview_sitecollectionurl', { 'SiteCollectionURL': siteUrl, 'UserID': userId });
}
function LoadGoogleAnalytics() {
//Do Some Stuff
//Then Call
LogGoogleAnalytics(gaUserID);
}
window.addEventListener ?
window.addEventListener("load",LoadGoogleAnalytics,false) :
window.attachEvent && window.attachEvent("onload",LoadGoogleAnalytics);
Am I missing something here? Is there a caching that gtag uses so it passes 'same' values when redirected to another page?

Unexpected token G in JSON at position 0 with Google Analytics

I am trying to use the Measurement Protocol from Google Analytics in my working Angular 5 project. I put the Google Analytics Universal code in index.html and I am making http calls to the service like this
index.html
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-XXXXXXXX-Y', {
'cookieDomain': 'none'
});
// console.log(window.dataLayer);
</script>
Service launching http calls
import { Injectable } from '#angular/core';
import { HttpClient, HttpParams, HttpHeaders } from '#angular/common/http';
import { DataService } from './Data.service';
#Injectable()
export class AnalyticsService {
private anUID = 'UA-XXXXXXXX-Y';
private analyticsURL = 'https://www.google-analytics.com/collect?';
constructor(private http: HttpClient, private datos: DataService) { }
public pageViewLista(): void {
this.http.get(
this.setScreenViewUrl(
encodeURI('Lista de empresas'))).subscribe((data) => {
console.log('Received data from GAnalytics: ' + JSON.stringify(data));
}
);
}
protected setScreenViewUrl (pantalla: string): string {
const constructUrl = `${this.analyticsURL}v=1&t=screenview&tid=${this.anUID}&cid=${this.datos.id}&an=${this.datos.app}&dt=${pantalla}&cd=${pantalla}`;
return constructUrl;
}
}
The problem is what Google returns an strange error and I dont know what it means nor the reason of this error. Am I doing a bad implementation?
Error from Google:
ERROR
HttpErrorResponse {headers: HttpHeaders, status: 200, statusText: "OK", url: "https://www.google-analytics.com/collect?v=1&t=...", ok: false, …}
error : {error: SyntaxError: Unexpected token G in JSON at position 0 at JSON.parse () at XMLHttp…, text: "GIF89a�����,D;"}
It seems like if the server was tryings to JSON parse a GIF image. Cant find anything in the documentation and Google shows no information.
Thanks a lot for your help.
The Google Analytics endpoint for data collection returns a transparent gif file (and it returns a 200 status for everything but server errors, so you can't use this to see if your data is actually tracked). A gif cannot be decoded as JSON.
If you want a JSON response you would need to use the endpoint for the GA debugger (google-analytics.com/debug/collect). That would give info if your payload is valid, but would not track the call.

GAPlugin in ionic framework

i am trying to build an ionic project that uses GAPlugin but my code is like this in index.html:
var gaPlugin; // Google analytics stuff
if(( /(ipad|iphone|ipod|android|windows phone)/i.test(navigator.userAgent) )) {
alert('hello world! this is a device');
document.addEventListener('deviceready', initApp, false);
} else {
alert('hello world! this is NOT a device');
initApp();
}
/**
* init google analytics
*/
function initApp() {
// Init google analytics
gaPlugin = window.plugins.gaPlugin;
gaPlugin.init(onGASuccess, onGAError, "UA-x-x", 10); // replace UA-x-x with your Tracking ID
}
I am trying to add Google Analytics in my ionic project and i included the GAPlugin, but i get this error:
Uncaught TypeError: Cannot read property 'exec' of undefined
Anyone have a clue what might be the problem?

Use existing sqlite database in cordova

Hello I'm creating a application with ionic framework and cordova and want to use a sqlite database to store the data.
So how can I use a existing sqlite database for this purpose?
Where do I have to save the database so that cordova can find it and uses it if I compile for a platform?
Edit:
I get the following error if I try to open the db in the recommend way:
[Error] TypeError: undefined is not an object (evaluating 'n.sqlitePlugin.openDatabase')
openDB (ng-cordova.min.js, line 9)
(anonyme Funktion) (app.js, line 19)
(anonyme Funktion) (ionic.bundle.js, line 44243)
onPlatformReady (ionic.bundle.js, line 2396)
onWindowLoad (ionic.bundle.js, line 2375)
Here is the head section of my index.html:
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<title></title>
<link href="lib/ionic/css/ionic.css" rel="stylesheet">
<link href="css/style.css" rel="stylesheet">
<!-- ionic/angularjs js -->
<script src="lib/ionic/js/ionic.bundle.js"></script>
<!-- cordova sqlite extenseion -->
<script src="js/ng-cordova.min.js"></script>
<!-- cordova script (this will be a 404 during development) -->
<script src="cordova.js"></script>
<!-- your app's js -->
<script src="js/app.js"></script>
<script src="js/controllers.js"></script>
</head>
My app.js looks like this:
//Database variable
var db = null;
angular.module('starter', ['ionic', 'starter.controllers', 'ngCordova'])
.run(function($ionicPlatform, $cordovaSQLite) {
$ionicPlatform.ready(function() {
// Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
// for form inputs)
if (window.cordova && window.cordova.plugins.Keyboard) {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
}
if (window.StatusBar) {
// org.apache.cordova.statusbar required
StatusBar.styleDefault();
}
db = $cordovaSQLite.openDB({ name: "db.spareParts" });
var query = "SELECT cars.name FROM cars";
$cordovaSQLite.execute(db, query).then(function(res) {
if(res.rows.length > 0) {
console.log("SELECTED -> " + res.rows.item(0).name);
} else {
console.log("No results found");
}
}, function (err) {
console.error(err);
});
});
})
The database db.spareParts is stored in the root www directory and is a sqlite3 database.
i fixed this problem after a very long suffering :)
the prepopulated database MUST be in the "dataDirectory" of your application
at first you need to check if the db file exists in that directory, if not, you must copy it from application directory (i.e. /www folder)
check this piece of code
$ionicPlatform.ready(function() {
src = cordova.file.dataDirectory + 'data.sqlite';
window.resolveLocalFileSystemURL(src, function () {
db = sqlitePlugin.openDatabase('data.sqlite');
}, function () {
$scope.copyDb('data.sqlite');
});
$scope.copyDb = function (dbName) {
var sourceFileName = cordova.file.applicationDirectory + 'www/' + dbName;
var targetDirName = cordova.file.dataDirectory;
return Promise.all([
new Promise(function (resolve, reject) {
resolveLocalFileSystemURL(sourceFileName, resolve, reject);
}),
new Promise(function (resolve, reject) {
resolveLocalFileSystemURL(targetDirName, resolve, reject);
})
]).then(function (files) {
var sourceFile = files[0];
var targetDir = files[1];
return new Promise(function (resolve, reject) {
targetDir.getFile(dbName, {}, resolve, reject);
}).then(function () {
console.log("file already copied");
}).catch(function () {
console.log("file doesn't exist, copying it");
return new Promise(function (resolve, reject) {
sourceFile.copyTo(targetDir, dbName, resolve, reject);
}).then(function () {
console.log("database file copied");
db = sqlitePlugin.openDatabase('data.sqlite');
});
});
});
};
check this link
prepopulated db demo
So how can I use a existing sqlite database for this purpose?
You can use the existing sqlite database by using the built-in WebSQL. You do not need to install any plugin. Please see the link here for more details.
Note that there is limitation to the amount of storage using the standard approach.(I think it is about 5MB).
If you want to store more than the allowed standard storage than you need to use the sqlite plugin. Link here for details.
Where do I have to save the database so that cordova can find it and uses it if I compile for a platform?
You do not need to specify the location path for storing the database. The database open function call will automatically creates and stores it in a convenient location.
Edit 1:
What I understand from your edited question is that you require a pre-populated sqlite3 db to be used for your application.
Copying your db to the www directory will not work. You need to copy the pre-populated db to the default location where the app will use it for your application. The location is different for different platforms (iOS, Android, Windows). Also within a platform the location path for the db storage is different. E.g for Android Jelly bean the location path is different to the one in Android KitKat. Hence as far as I know you will not a find a complete solution on the Web for copying the db to the default location of the app.
This is a completely a different proposition.
You need to search stackoverflow for pre populated sqlite with cordova to get useful answers.
What I have answered is to create a new db in your application from scratch.
Also relating to your error.
You need to check that the deviceready event is fired before calling any cordova/phonegap related code.
if(window.cordova) {
// App syntax
db = $cordovaSQLite.openDB("myapp.db");
} else {
// Ionic serve syntax
db = window.openDatabase("myapp.db", "1.0", "My app", -1);
}

Resources