GTM does not reset dataLayer on page navigation - next.js

I am trying to integrate GTM into my single page NextJS app. I inject my script into the _document.js as suggested:
export default class MyDocument extends Document {
render() {
return (
<Html>
<Head>
<script
dangerouslySetInnerHTML={{
__html: `window.dataLayer = window.dataLayer || [];`
}} />
<script
dangerouslySetInnerHTML={{
__html: `(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
' https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','${getGtmTrackingId()}');`,
}} />
</Head>
<body>
<noscript
dangerouslySetInnerHTML={{
__html: `<iframe src="https://www.googletagmanager.com/ns.html?id=${getGtmTrackingId()}" height="0" width="0" style="display:none;visibility:hidden"></iframe>`,
}}
/>
<Main />
</body>
</Html>
My question is this: every time i navigate through pages, I see that dataLayer still contains the events that were fired on previous pages. Do I need to reset my dataLayer on every url change? Do I reach the limits of GTM if I do not reset my dataLayer on url change or is it okay to include all events that fired?
I tried resetting the dataLayer like this, but if I do that gtm stops sending events.
In my _app.js file:
useEffect(() => {
router.events.on('routeChangeStart',() => {window.dataLayer = []});
}, [router.events]);
Thanks.

try this on before you push a page view event:
window.dataLayer.push(() => {
window.google_tag_manager["your_container_id"].dataLayer.reset()
})

Related

Integrating ShareThis with Next.js

Has anyone tried to integrate ShareThis with Next.js?
I'm getting "Hydration failed because the initial UI does not match what was rendered on the server." and this, I have ascertained, is down to the inclusion of the ShareThis script tag.
I'm not sure what I need to do in order to resolve this error.
This is my _document.tsx file, containing the offending script tag:
import { Html, Head, Main, NextScript } from "next/document";
const Document = () => (
<Html>
<Head>
<script
type="text/javascript"
src={`https://platform-api.sharethis.com/js/sharethis.js#property=${process.env.NEXT_PUBLIC_SHARETHIS_PROPERTY_ID}&product=sop`}
async
defer
></script>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
export default Document;
Of course, the NEXT_PUBLIC_SHARETHIS_PROPERTY_ID variable from my .env file is being correctly populated.
You should be using next/script to load 3rd party scripts like ShareThis. Here's their documentation https://nextjs.org/docs/basic-features/script
Make sure to apply the <Script src="" /> component OUTSIDE of the next/head component, either above of below it.
Hope this helps.

next script does not work inside _document.js file nextjs

I have a nextjs project. I want to load two scripts using next/script inside _document.js.But when I place the Script tags into the body tag in _document.js, my scripts do not execute. I implemented according to the next/script guideline.What may be the issue here?
My code:
import { Html, Head, Main, NextScript } from "next/document";
import Script from "next/script";
export default function Document() {
return (
<Html>
<Head>
<link rel="preconnect" href="https://fonts.googleapis.com" />
</Head>
<body>
<Main />
<NextScript />
<Script
strategy="beforeInteractive"
src="src"
type="text/javascript"
charSet="UTF-8"
data-domain-script="id"
/>
<Script
strategy="beforeInteractive"
type="text/javascript"
dangerouslySetInnerHTML={{
__html: `
some js code
`,
}}
/>
</body>
</Html>
);
}
Initially in next/script api reference section, it was documented to use Script tags in _document.js with beforeInteractive strategy. But now their api reference has been updated and you must use Script tag with beforeInteractive strategy in pages/_app.js.
API Reference Link
Quote from next/script api reference
beforeInteractive scripts must be placed inside pages/_app.js and are designed to load scripts that are needed by the entire site (i.e. the script will load when any page in the application has been loaded server-side).

Cannot get user info with Google Identity Services

Well, I'm integrating the Google Identity Services into my application. But it doesn't run.
I'm following the instruction give in https://developers.google.com/identity/gsi/web
So I added these lines into the head of the index.html file
<meta name="google-signin-client_id" content="MY_GOOGLE_ID.apps.googleusercontent.com">
<script src="https://accounts.google.com/gsi/client" async defer></script>
and these at the beginning of the body:
<script>
function handleCredentialResponse(response) {
console.log('hello, world');
console.log("Encoded JWT ID token: " + response.credential);
}
window.onload = function () {
google.accounts.id.initialize({
client_id: 'MY_GOOGLE_ID.apps.googleusercontent.com',
callback: handleCredentialResponse
});
// Display the One Tap prompt
google.accounts.id.prompt();
// Display the Sign In With Google Button
google.accounts.id.renderButton(
document.getElementById("buttonDiv"),
{ theme: 'outline', size: 'large' }
);
}
</script>
Then added this line into my Navbar.js
<li className='nav-item'>
<div id="buttonDiv" class="g-signin2" data-onsuccess="onSignIn"></div>
</li>
As far as I understood, when the user correctly sign-in, the handleCredentialResponse callback should be called by Google, but nothing appeared on my console.log.
I can login correctly, using different credentials. The user image and email address appear into the button, but I'm not notified of the event.
I tried to follow the instruction, but seems that Google suggest these new approach referred in the link at the top, but the instruction still are for the old implementation.
Can anyone help, please?
So when you initialise the button in your tag, it will have a "data-callback" parameter. Pass your handler in that like below.
<html>
<body>
<script src="https://accounts.google.com/gsi/client" async defer></script>
<div id="g_id_onload"
data-client_id="YOUR_GOOGLE_CLIENT_ID"
**data-callback="handleCredentialResponse"**
data-login_uri="https://your.domain/your_login_endpoint"
data-auto_prompt="false">
</div>
<div class="g_id_signin"
data-type="standard"
data-size="large"
data-theme="outline"
data-text="sign_in_with"
data-shape="rectangular"
data-logo_alignment="left">
</div>
</body>
</html>

Next 11 and adding Script tags not working. No scripts are rendered

I added my google tag manager to _app.js file, and its not showing. None of the scripts I am loading via the new "Script" tag are working.
<Head>
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" />
<Script
src={`https://cdn.somelink.coom/${process.env.COOKIE_KEY}/block.js`}
strategy="beforeInteractive"
/>
<Script
src="https://cdn.somelink.com/scripttemplates/stub.js"
strategy="beforeInteractive"
/>
<Script
src={`https://www.googletagmanager.com/gtag/js?id=${process.env.GOOGLE_KEY}`}
strategy="afterInteractive"
/>
These are not working. Nothing is downloaded in the network tab etc. Nothing shows up on the page. Any thoughts?
Reminder: this is in the _app.js file.
Note: My pages are static generated.
next/script should not be wrapped in next/head
Ref.: Script Component
Do something like this:
import Script from "next/script";
const App = ({ Component, pageProps }) => (
<>
<Script
id="scriptAfterInteractive"
src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"
/>
{/* eslint-disable-next-line react/jsx-props-no-​spreading */}
<Component {...pageProps} />
</>
);
export default App;
In addition to the requirement that Script (next/script) not be wrapped in Head (next/head), it should also not be placed within a custom _document.tsx unless you are not passing strategy="beforeInteractive" as mentioned in the docs.
I was unable to load a Cloudflare analytics script when placing it in _document.tsx, but it loaded successfully as a sibling to Component in _app.tsx.

"TypeError: undefined is not a function" when trying to push() to Firebase

Upon submission of a form, I want to push that data to my Firebase db and so I'm creating a function to do so (addMeeting). However, upon pressing the button to submit I get the error:
TypeError: undefined is not a function
at l.$scope.addMeeting (http://localhost:8000/js/controllers/meetings.js:10:12)
meetings.js:10:12 is right where my $push is if you'll look at my code below.
My HTML:
<!doctype html>
<html lang="en" ng-app="myApp">
<head>
<meta charset="UTF-8">
<title>Angular Data</title>
<meta name="viewport" content="width=device-width, userscalable=no">
<link rel="stylesheet" href="css/style.css">
<!-- AngularJS -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script src="js/lib/angular/angular-route.min.js"></script>
<script src="js/lib/angular/angular-animate.min.js"></script>
<!-- Firebase -->
<script src="https://cdn.firebase.com/js/client/2.2.2/firebase.js"></script>
<!-- AngularFire -->
<script src="https://cdn.firebase.com/libs/angularfire/1.0.0/angularfire.min.js"></script>
<script src="js/app.js"></script>
<script src="js/controllers/registration.js"></script>
<script src="js/controllers/meetings.js"></script>
</head>
<body>
<header>
<nav class="cf" ng-include="'views/nav.html'"></nav>
</header>
<div class="page">
<main class="cf" ng-view></main>
</div>
</body>
</html>
My apps.js:
var myApp = angular.module('myApp',
['ngRoute', 'firebase', 'appControllers']);
var appControllers = angular.module('appControllers', ['firebase']);
myApp.config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/login', {
controller: 'RegistrationController',
templateUrl: 'views/login.html'
}).
when('/register', {
controller: 'RegistrationController',
templateUrl: 'views/register.html'
}).
when('/meetings', {
controller: 'MeetingsController',
templateUrl: 'views/meetings.html'
}).
otherwise({
redirectTo: '/login'
});
}])
meetings.js -the Controller containing the addMeeting function that is failing:
myApp.controller('MeetingsController',
function($scope, $firebaseObject) {
var ref = new Firebase('https://angulardataldc.firebaseio.com/meetings');
var meetings = $firebaseObject(ref);
$scope.meetings = meetings;
$scope.addMeeting = function() {
meetings.$push({
name: $scope.meetingname,
date: Firebase.ServerValue.TIMESTAMP
})
}
}); //MeetingsController
The view that is calling that function upon submission of a form:
<section class="meetings cf">
<h1>Add Meetings</h1>
<form class="formgroup addmeeting cf"
name="myform"
ng-submit="addMeeting()"
novalidate>
<div class="inputwrapper">
<input type="text" name="meetingname" placeholder="Meeting Name"
ng-model="meetingname" ng-required="true">
</div>
<button type="submit" class="btn"
ng-disabled="myform.$invalid">+</button>
</form>
<h2>Your Meetings</h2>
<div class="meeting" ng-repeat="meeting in meetings">
<p>{{meeting.name}}</p>
</div>
</section>
**Edit: ** It has something to do with the .push() method itself. I see that in the latest version of angularfire/firebase it should be .push, instead of .$push, ad have changed that but it does not solve my problem. I reverted AngularFire and Firebase to versions 0.8.2 and 1.0.21 respectively, re-introduced the .asObject() and $push, and everything works fine. I don't understand why .push() is failing with all the latest (Firebase 2.2.2, AngularFire 1.0).
Firebase's AngularFire library has two primary types: $firebaseObject and $firebaseArray (instantiated through $asObject and $asArray respectively in pre-1.0 versions of AngularFire).
You're using both the wrong type and the wrong method. To quote AngularFire's documentation on its array type:
Synchronized arrays should be used for any list of objects that will be sorted, iterated, and which have unique IDs. The synchronized array assumes that items are added using $add(), and that they will therefore be keyed using Firebase push IDs.
So:
var ref = new Firebase('https://angulardataldc.firebaseio.com/meetings');
var meetings = $firebaseArray(ref);
$scope.meetings = meetings;
$scope.addMeeting = function() {
meetings.$add({
name: $scope.meetingname,
date: Firebase.ServerValue.TIMESTAMP
})
}
You made a typo, it should be .push instead of $push
CODE
$scope.addMeeting = function() {
meetings.push({
name: $scope.meetingname,
date: Firebase.ServerValue.TIMESTAMP
})
}
Reference

Resources