I try to update my Google Analytics implementation from analytics.js to the new gtag.js.
In the old implementation I am using the ready callback function.
ga(function() {
console.log('Google analytics is ready');
});
How can I implement a ready callback in the new gtag.js? I can't find any information in Google's documentation.
The command event supports the parameter event_callback, a function called when processing has completed. So, compared to the old analytics.js, you need to send an event to trigger the callback.
For example, you can use the page_view event; however, to avoid duplicates, you must disable the automatic page_view event by setting send_page_view to false.
gtag('config', GA_TRACKING_ID, {
'send_page_view': false
});
gtag('event', 'page_view', {
'event_callback': function() {
console.log('Google Analytics is ready');
}
});
A simpler (but probably less reliable) solution is to use the onload attribute of the <script> tag:
<script async src="https://www.googletagmanager.com/gtag/js?id=GA_TRACKING_ID"
onload="console.log('Google analytics is ready');">
</script>
Related
I have a mobile app created with Ionic and angularJS.In the app i'm using websql database to save data locally in device.when call my function(which is with a callback) i need move to another page(homepage).this is not happening with callback.can someone help me to figure this out.(Im using apprey to build my application)
Below is my javascript function with call back function as websql is asynchronous by its nature.
var logbool;
ValidateUserLocalCookie(success,function(isLogged)
{
console.log("Logged"+isLogged);
**Apperyio.navigateTo("Home", {});///---this function should redirect to home**
});
and below is the websql function.
function ValidateUserLocalCookie(success,callbackFunc)
{
logbool=false;
db = openDatabase(shortName, version, displayName, maxSize);
db.transaction(function(tx)
{
var boole=false;
tx.executeSql('select * from Userlog;',[],function(tx,table){
console.log(table.rows.length);
if(table.rows.length>0){
logbool=true;
callbackFunc(logbool);
}
else{
logbool=false;
callbackFunc(logbool);
}
});
});
}
By default, Ionic apps use the ui-router. Leverage that. You should have a list of states predefined in your router code.
Once that's done, simply inject the $state service into your controller. Inside of the callback, use it to set your state. Here is some code that does something similar.
function saveClass() {
$log.log('Saving class.');
ClassService.save(vm.class)
.then(function () {
$state.go('tabsController.classes');
})
.catch(function (err) {
$log.log(err);
});
}
My ClassService calls an asynchronous database function, which I have exposed as a promise. You can also use a callback. Upon completion of the save, I call $state.go(<state-name>); In the event of an error, I remain on the same page.
For example, a page contains multiple controllers and directives each making separate $http request to populate their data.
Is there a global event we can subscribe to that will be triggered when Angular finishes calling all callbacks (i.e. after all $http requests are complete and all callbacks have been executed)?
Turns out there is an undocumented angular service called $browser that accomplishes this and is used by protractor.
waitForAngular = function(el, angular, callback) {
try {
angular.element(el).injector().get('$browser').notifyWhenNoOutstandingRequests(callback);
} catch (e) {
callback(e);
}
};
https://www.snip2code.com/Snippet/91565/Wait-until-angularjs-is-done-with-proces
Imagine I have a controller which handles, for example, view changes:
function Controller($scope){
var viewModel = this;
viewModel.goBack= function(){
viewModel.visible = visibleLinks.pop(); //get last visible link
viewModel.swipeDirection = 'left';// for view change animation
}
}
But I want to handle it not only for example with HTML buttons inside <body>, but also with Back button on device. So I have to add Event Listener for deviceready event, and also explicit call $scope.$apply() in order to fact, that it is called outside of AngularJS context, like this:
document.addEventListener("deviceready", function(){
document.addEventListener("backbutton", function(){
viewModel.goBack();
$scope.$apply();
}, false);
}, false);
}
But I also want to follow (relatively :) ) new controllerAssyntax, cause this is recommended now e.g. by Todd Motto: Opinionated AngularJS styleguide for teams and it allows to remove $scope from controllers when things like $emit or $on are not used. But I can't do it, case I have to call $apply() cause my context is not Angular context when user clicks on device back button. I thought about creating a Service which can be wrapper facade for cordova and inject $scope to this service but as I read here: Injecting $scope into an angular service function() it is not possible. I saw this: Angular JS & Phonegap back button event and accepted solution also contains $apply() which makes $scope unremovable. Anybody knows a solution to remove Cordova specific events outside Angular controller, in order to remove $scope from controllers when not explicity needed? Thank you in advance.
I don't see a reason why to remove the $scope from the controller. It is fine to follow the best practice and to remove it if not needed, but as you said you still need it for $emit, $on, $watch.. and you can add it $apply() in the list for sure.
What I can suggest here as an alternative solution is to implement a helper function that will handle that. We can place it in a service and use $rootScope service which is injectable.
app.factory('utilService', function ($rootScope) {
return {
justApply: function () {
$rootScope.$apply();
},
createNgAware: function (fnCallback) {
return function () {
fnCallback.apply(this, arguments);
$rootScope.$apply();
};
}
};
});
// use it
app.controller('SampleCtrl', function(utilService) {
var backBtnHandler1 = function () {
viewModel.goBack();
utilService.justApply(); // instead of $scope.$apply();
}
// or
var backBtnHandler2 = utilService.createNgAware(function(){
viewModel.goBack();
});
document.addEventListener("backbutton", backBtnHandler2, false);
});
In my case I was simply forwarding Cordova events with the help of Angular $broadcast firing it on the $rootScope. Basically any application controller would then receive this custom event. Listeners are attached on the configuration phase - in the run block, before any controller gets initialized. Here is an example:
angular
.module('app', [])
.run(function ($rootScope, $document) {
$document.on('backbutton', function (e) {
// block original system back button behavior for the entire application
e.preventDefault();
e.stopPropagation();
// forward the event
$rootScope.$broadcast('SYSTEM_BACKBUTTON', e);
});
})
.controller('AppCtrl', function ($scope) {
$scope.$on('SYSTEM_BACKBUTTON', function () {
// do stuff
viewModel.goBack();
});
});
Obviously in the $scope.$on handler you do not have to call $scope.$apply().
Pros of this solution are:
you'll be able to modify an event or do something else for the entire application before the event will be broadcasted to all the controllers;
when you use $document.on() every time controller is instantiated, the event handler stays in the memory unless you manually unsibscribe from this event; using $scope.$on cares about it automatically;
if the way a system dispatches Cordova event changes, you'll have to change it in one place
Cons:
you'll have to be careful when inheriting controllers which already have an event handler attached on initialization phase, and if you want your own handler in a child.
Where to place the listeners and the forwarder is up to you and it highly depends on your application structure. If your app allows you could even keep all the logic for the backbutton event in the run block and get rid of it in controllers. Another way to organize it is to specify a single global callback attached to $rootScope for example, which can be overriden inside controllers, if they have different behavior for the back button, not to mess with events.
I am not sure about deviceready event though, it fires once in the very beginning. In my case I was first waiting for the deviceready event to fire and then was manually bootstrapping AngularJS application to provide a sequential load of the app and prevent any conflicts:
document.addEventListener('deviceready', function onDeviceReady() {
angular.element(document).ready(function () {
angular.bootstrap(document.body, ['app']);
});
}, false);
From my point of view the logic of the app and how you bootstrap it should be separated from each other. That's why I've moved listener for backbutton to a run block.
$scope.authorizedUsers = [];
fbRef.child('folders/').child($routeParams.folderID).once('value',
function(ss) {
ss.forEach(function(childSnapshot) {
fbRef.child('users/').child(childSnapshot.name()).once('value',
function(user) {
$scope.authorizedUsers.push(user.val());
console.log($scope.authorizedUsers);
});
});
});
I use above code to look up master data then loop them in view using ng-repeat
<div ng-repeat="user in authorizedUsers">{{user}}</div>
but the looped data display sometimes only. Sometime means I have to refresh several time to get the data display.
Any idea or there is other better way to look up master data in firebase?
The callbacks are invoked asynchronously, so they don't happen within Angular's digest scope. Thus, they are not going to be applied to the page until a new compile occurs.
The solution that angularFire uses internally is to call $timeout(), which will force Angular's compile process to run. Some experimentation would reveal the most efficient way to get this done. Here's a brute force:
$scope.authorizedUsers = [];
fbRef.child('folders/').child($routeParams.folderID).once('value',
function(ss) {
ss.forEach(function(childSnapshot) {
fbRef.child('users/').child(childSnapshot.name()).once('value',
function(user) {
$timeout(function() {
$scope.authorizedUsers.push(user.val());
console.log($scope.authorizedUsers);
});
});
});
});
I am using the insertPre plugin. I don't use a SAVE button and rely on the CKEditor always keeping my back-end data in sync.
When a user enters data into the insertPre plugin and then clicks okay the contents of the insertPre are moved to the main edit area.
However I am using AngularJS and I need to have the backend data object updated when insertPre returns. Here is my solution but I am not sure it's a good one:
ck.on('insertElement', function () {
//$scope.$apply(function () {
// ngModel.$setViewValue(ck.getData());
//});
setTimeout(function () {
$scope.$apply(function () {
ngModel.$setViewValue(ck.getData());
});
}, 2000);
});
It seems messy to wait two seconds for the textarea of CKEditor to be fully updated and then be doing an update to my backing data field.
Does anyone have any other solutions to suggest. Ideally I would like to be able to trigger on some event AFTER the insertElement has updated the screen.
Note that I have tried the following and this does not work as I guess it's not fired.
ck.on('pasteState', function () {
$scope.$apply(function () {
ngModel.$setViewValue(ck.getData());
});
});
I have not verified this, so test before accepting and if it fails I'll remove this A.
I'm assuming that the problem is that the change has not het actually happened when inertElement fires?
If you are using CKEditor 4.2+ you have access to a change event. I see that insertPre is 4.1 only, but I wonder if insertElement fires the change event internally after it has finished, it to me it would seem rational. So you could try something like this and follow the console to see how often it fires.
ck.on('change', function () {
console.log('change event!');
$scope.$apply(function () {
ngModel.$setViewValue(ck.getData());
});
});
If it's too often, just do a delay by resetting a 500ms or so timer every time this event happens and only after it gets to the end you fire update the Model - that way you won't cripple your backend/performance.