google analytics page views are being incremented incorrectly for each page change (angularjs) - angularjs

I'm using Google analytics for my website (built using AngularJS) to track page views. For some reason though, the page views are incremented incorrectly. For example, when I'm on home page and I switch to the about page, about page gets 1 view, which is correct, but when I switch to another page, that page gets 2 views when it should just get 1. When I switch to another page, that page gets 3 views and so on until I reload the website. Reloading the website will reset the incrementation back to 1, and it'll start counting up again, which means there appears to be a count that is incremented with each state change.
I have this code in all the controllers for each page:
$rootScope.$on('$stateChangeSuccess', function(event) {
if (!$window.ga)
return;
$window.ga('send', 'pageview', { page: $location.path() });
});
What is the cause of the tracking error and how can I resolve it?

$stateChangeSuccess is a global event, which means that if you include the above code in every controller, every time a new controller is instantiated, you are creating a new listener for the event. When a state change occurs, every listener you have registered gets fired, thus the increasing number of calls that are happening.
You actually only need to do this once, probably in your main module run() method, rather than in all of your controllers.

Related

Run a function only the first time when AngularJS gets loaded

I'm using angular-seed template (AngularJS) and I have routeProvider setup and such, now I want to execute a function (which resides in a factory), but only the very first time when the page gets loaded. I found many solutions for this, but I don't want to execute the code each time the users switches between tabs (via routeProvider of course, page doesn't get reloaded) - the code must be executed only when the whole page gets (re)loaded.
How should I approach this? I tried to call the function from run and then broadcast the event when page gets loaded, but there are no event listeners - I guess that is because the run part gets executed before the controllers are setup, so there are no listeners attached at the time when the event gets broadcast.
So, any suggestions how to approach this?
UPDATE
Use case:
when user types the url in the page, the page gets loaded
when pages gets loaded, a $http.get request is performed, which gets a random content
this content can be changed only by clicking a button, to explicitly request a change of content.
if users clicks to a different page e.g. view2 (routeProvider) and then back to the view1, the content must not change
when users refreshes the page (F5), the content changes again (or as already stated, by a click of a button)
Use the run method:
app.run(function(yourService){
yourService.cacheRandomContent();
});
It runs only once after the app is bootstrapped and services are created.
To access the data in controller, just inject the same service:
app.controller('someCtrl',function($scope, yourService){
yourService.getCachedRandomContent().then(function(resp){
$scope.data = resp.data;
});
});
Your service would be something like:
app.service('yourService',function($http){
var promise;
return {
cacheRandomContent: function(){
promise = $http.get();
},
getCachedRandomContent : function(){
return promise;
}
}
});

kendoRendered triggered multiple times

I have the following code in my Angular controller:
$scope.$on("kendoRendered", function(e) {
console.log('final');
});
The result is that final is logged three times. It may be due to Kendo elements in directives? I'm looking for a way to have a final instance to know that the UI elements are already loaded and start filling out the data.
UPDATE
I use $state.go (state, params ); (Angular ui-router service) to open the page (that has a related controller). First time I open the page (after refresh) final is logged three times, if I close and open the page again, final is logged once.

Browser back button doesn't trigger reload after using Angular's $location.path() update

Situation:
On one of the views of my AngularJS app I have two pagination buttons that use $http to request the next and previous data. I'm using ng-click="getNext(url)" rather than an anchor tag with href="#/items/:itemId". The reason I'm doing this is so that I can quickly page through my content asynchronously w/o triggering a page reload. This works just fine, but using this method bypasses updating the page's URL so its possible to have your current content out of sync with your URL's id (i.e. path is #/items/3 but you're currently viewing item 9). This can be easily fixed by updating the URL in the JS using $location.path('items/' + rsp.id). Now I can fetch data in an async manner and still be able to refresh, bookmark, send links and have the correct/current item display.
Problem:
The problem with this is if a user hits getNext() a few times and then tries to go back using the browser's back button the URL updates like it should but for some reason the browser doesn't perform a refresh–it just sits there and updates the URL. This only occurs with when the item in history is from the same view and I have updated the ID with the location service.
Any ideas would be appreciated.
What I tried so far
Promises + Flags
I've been playing with window.onpopstate, but as of right now I don't have any way to have window.onpopstate differentiate between a browser click and a UI click that updates the URL with $location.path(); Right now it fires the event regardless of the source. So I tried setting a flag to assume that every time this event fires its a browser event, but when its a UI-based event I can disabled that flag because my _myRequestFn() will handle it. Even with this promise setup it still fires the window.onpopstate when _myRequestFn() is fired.
var flag = true; // Assume all requests are browser-based
window.onpopstate = function() {
if (flag) {
$route.reload();
}
console.log('onpopstate fired');
};
_myRequestFn = function(id) {
someService.getMyData(id)
.then(function(rsp) {
// Do a bunch of stuff including...
$location.path('items/' + rsp.id);
return rsp;
})
.then(function() {
// Everything is done reset flag
flag = true;
});
};
$scope.getNext(url) {
flag = false;
_myRequestFn(url);
};
Spoofing
Hitting back through #/item/5 > #/item/4 > #/item/3 just updates the URL and not the path, but if the history has a different param #/thing/2 > #/item/2 that triggers a page refresh. Since the browser back button works if the history is from a different param I wanted to see if I loaded a different param it would work. So I created an #/item-a and #/item-b route that loaded the same template and used the same controllers, just toggled from a/b with each request. I would never recommend this solution to someone, I was more just seeing if I could get the refresh to trigger.
Update
Lots of people on IRC are suggesting that I use UI-Router. I'm really trying to use the out of the box Angular solution. Refactoring my whole routing setup to use UI-Router is not an optimal solution.
app.config(['$locationProvider',
function($locationProvider) {
// Enable html5 mode
$locationProvider.html5Mode(true);
}]);
From the docs:
In HTML5 mode, the $location service getters and setters interact with the browser URL address through the HTML5 history API. This allows for use of regular URL path and search segments, instead of their hashbang equivalents.

UI Router fails to update URL in address bar after successful state change

I'm building a small Angular app and I'm currently using ui-router.
I've hit a problem which looks awfully like a bug in ui-router but I can't be sure as I'm not that familiar with this library. When the user clicks on a specific link, although the correct view state gets loaded as expected, the URL in the address bar doesn't get updated.
I'm using ui-router's ui-sref directive to automatically generate the URL for the state. For example in the checklist list view I use the following code:
<a ui-sref="checklist-phase({ aircraftId: checklist.aircraft, checklistId: checklist.id, phaseSlug: checklist.phases[0].slug })" ng-bind="checklist.name"></a>
I've cut down my app and made it into a Plunker so the problem is hopefully reproducible by others. The issue can also be observed in this video: https://www.youtube.com/watch?v=EW9CFe6LfCw
Reproduction steps:
Go to http://run.plnkr.co/plunks/ZqMIYNU6abEAndAM5nx1/#/aircraft/1/checklists
Click the first link. Notice that the view updates to show the correct state, but the URL remains at /#/aircraft/1/checklists.
What is strange is that navigating back to this state by other means updates the URL perfectly. For example (assuming steps 1 and 2 above have been followed):
Scroll to the bottom and click the Next Phase link. Note that the state changes and the URL updates.
Scroll down on this new view and click Previous Phase. Note that the previous state reloads and this time the URL it updated correctly.
Am I using ui-router incorrectly or doing something else incorrectly to cause this behaviour?
Check here updated version
On your state 'check-lists' you provide ui-sref to 'checklist-phase'
<a ui-sref="checklist-phase({ aircraftId: ...
And the 'checklist-phase' is defined as a child state of 'checklist-detail'
.state('checklist-phase', {
parent: 'checklist-detail',
And the state 'checklist-detail' has controller which calls $state.go
.state('checklist-detail', {
controller: 'ChecklistDetailCtrl',
...
.controller('ChecklistDetailCtrl', function ($scope....
{
$state.go('checklist-phase', {
phaseSlug: checklistData.phases[0].slug
}, {
location: 'replace'
});
Do NOT do that... just remove the $state.go - because you are already navigating to the checklist-phase (see the first lines above) ... check it here

$locationChangeStart never broadcasted; $browser.onUrlChange never called after page load

My angular app is non-standard in that we don't use route provider or UI router. WE use a durandal style of navigation that swaps out views using $http.
locationChangeStart never fires I think due to the fact that we are not changing our URL once in the SPA (though we plan to do so in the future sometimes). I can't test this, but if we remain in the SPA and we change the URL to point to a new view thru code (again, not using routeProvider/UiRouter) should I expect this event to fire?
My main issue is that I cannot handle these events: manual url change, back/forward/history buttons, external link clicks
I see $browser#onUrlChange is the function that broadcasts the $locationChangeStart event but that function only gets called on page load for me, but it seems to be exactly the code that should be getting called.
Documentation:
$browser#onUrlChange
It's only called when the url is changed from outside of angular:
user types different url into address bar
user clicks on history (forward/back) button
user clicks on a link
It's not called when url is changed by $browser.url() method
It says this part also:
NOTE: this api is intended for use only by the $location service. Please use the {#link ng.$location $location service} to monitor url changes in angular apps.
I don't know how to 'use $location service to monitory url changes', so I am listening to the $locationChangeStart event:
$rootScope.$on('$locationChangeStart', function(event, next, current) {
// Breakpoint hit here on page load only
var x = 0;
}
Why doesn't the onUrlChange/$locationChangeStart functions run for me?
thanks!

Resources