So we just relaunched our site as a Backbone powered single-paged app, but we're having a heck of a time tracking our conversions from our AdWords ads into the site with Google Analytics.
The problem is that in order for Google Analytics to track the fact that the user came in from AdWords, it looks for a URL query parameter called gclid in the URL:
http://test.com/?gclid=(Q#kjsdf0INKJSDJF9
But, when the Router from Backbone initializes, it removes all the query parameters from the URL, so when the tracking event fires, it doesn't see that the user came from an AdWords ad.
We tried sending the user to an interstitial page that loads the analytics code and waits for the event to fire and then forwards them into the site, but
Its ugly and shows users a blank page for a while
Doesn't work without a significant wait for IE8 users (who make up 50% of our user base sadly)
We contacted Google's AdWords help to see if we can manually give the gclid to Analytics and their answer was
You should hire one of our consultants who will figure out how to keep the URL parameters on your site so it works correctly because we do not support manually setting the gclid ID
(Sadly only a very minor paraphrase.)
Google-fu leaves me blank in this respect -- lots of people asking questions in the Google forums, but no answers.
I don't know how everything is setup, but Backbone might not be seeing that URL with the parameters as a match, or a route that it knows about. In fact, you can test that by doing something like...
unless Backbone.history.start(pushState: true)
window.location = someDefaultURL;
If you wanted just a simple way to match routes with params, here's two ways that you could do it.
class MyRouter extends Backbone.Router
routes:
'test?*params': 'test'
'testTwo?gclid=:gclid': 'testTwo'
test: (params) ->
console.log 'test route', params
testTwo: (gclid) ->
console.log 'test route two', gclid
So the issue here is that I was immediately pushing an entry into the history object with the navigate method.
This overwrites (correctly) the current URL and does not preserve URL parameters (although it would be nice if it could, I guess that's a pull request I should make).
By not calling navigate immediately this allows the URL parameter to stay in the URL long enough for G.A. to see it.
Related
I am trying to integrate segment.io to work with Angular. My angular app is a SPA so I have to add some code to my components to allow segment to register the page visits.
In the code used to register page visits, there is a property called referrer that, according to the docs is:
Full URL of the previous page. Equivalent to document.referrer from
the DOM API.
The problem I am having is the referrer property value sometimes just remains empty, or sticks to one page even if I move to several other different pages.
So my questions are:
Does referrer mean links outside my app or also the previous routes visited within my app?
Am I supposed to set this manually in the app?
Yes, you're supposed to set it manually in your existing pageview hits to Segment.
Basically, in your case, referrer would be just the previous route. If the previous route doesn't exist, then use the document.referrer.
And yes, organically, if your app doesn't have a previous route, then document.referrer may contain an outside url, which is completely fine and expected.
Also, Segment provides pretty neat support for questions like this, so you can confirm with them.
The problem:
I have a express backend/angularjs frontend web app hosting all of my teams documentation for our users. We frequently need to post links to a specific question (all linked as ids) to our users in Slack. ex. helpapp.com/productTheyNeedHelpWith#QuestionLinkedTo the problem is that when the link is clicked from slack, not when copy/pasted, Slack launches a new browser window/tab to helpapp.com/productTheyNeedHelpWith%23QuestionLinkedTo.
This breaks ngRoute as it no longer recognizes /productTheyNeedHelpWith as the path to load the correct page for and the user gets sent to our home page instead.
Thoughts on possible solutions:
I'd like to solve this issue either in the ngRouting itself by somehow updating the path by replacing %23 with # before $routeProvider does it's magic, or by somehow capturing the full url in app.js and updating it or redirect to an updated version before we've rendered a page view at all.
currently I've set a workaround by using a ng-init event to check the url and, if %23 is found, update it and replace it with the correct extension but this causes an odd double load that leads to a poor user experience.
Final Thoughts:
As I'm not sure of the best way to go about this, and because our code is proprietary, I'm avoiding posting to much 'example code' that may not be relevant but am happy to provide any parts of it that you, wonderful stackoverflowers, may request. Thank you in advance for your assistance.
We have a site that gets traffic from adwords. When adwords link clicked landing page of the site have gclid=xxxx extra parameter to track stats.
After landing page, if user navigates away from that page (still staying in the site) gclid parameter are not added to following page URLs.
I checked some other sites' adwords campaigns and saw that gclid remains in the URL no mater how many pages navigated.
Is this a feature by adwords or do I need to pass gclid parameter manually from page to page?
If I need to pass manually, is there a way to accomplish this with javascript? Because it is very hard to edit the CMS framework to add the parameter for all the links.
Note: Our advertisement company tells they are observing too many "direct/none" source in analytics and claims that this is because of the gclid parameter not passing through pages. Do you think is this the case
Your post contains a number of questions:
Is this a feature by adwords
No, Adwords offers no such feature.
If I need to pass manually, is there a way to accomplish this with javascript?
Yes, you could check for the existence of a gclid parameter in the document's URL and dynamically append it to any internal links on the page. But that's not required (see below).
... too many "direct/none" source ... because of the gclid parameter not passing through pages. Do you think is this the case
No, that's not the case. The linking between Adwords and Analytics depends on the gclid parameter being present on the initial hit of a session, where the Analytics script will pick it up and associate it with that user. Going forward, any direct hit from the same user will still be attributed to Adwords (up until the campaign timeout is reached). Maybe this Google Analytics documentation page will clear things up.
You can store the GCLID value in Session Storage or Cookies.
Decode the GCLID value and keep it as a hidden value in any of the forms on your website.
When developing a web application with angular js, a part of time that developers spend is the time for implementing routing.
When using ui-router in a application, there are two "phases" to consider with regards to routing:
user navigates inside application: when click is made on some button, user is transfered to another state by using $state.go("somestate"). Parameters can be send etc. And url is changed accordingly.
user navigates directly via url.
Lets say application has such route:
/mythings/{thingid}/mysubthings/{mysubthingid}
If user navigates to that url directly by pasting it into browser window, application needs to handle it. My question is what's the best practice to do it?
What I'm thinging is: if looking at url example above what needs to be done when user enters that url in browser:
get {thingid} from url (from $stateParams), then get {mysubthingid} also from $stateParams (probably by using resolve (ui-router feature) when defining state), then inject what was resolved to controller and then make a query to api and get data about "subthing" and present view in ui with that data. So that should work with both "types of navigations": when user clicks and is transfered to state, or when user enter url directly into browser. Is this the right path to go?
And I suppose that any url you go to when you click something in application, you should be able to take that url and just paste it into browser and see the same results without being redirected to anywhere else. If application cannot handle every url in such way, maybe application' architecture needs to be reconsidered?
Thanks
In UI-Router, the main component of the routing is the state. The URL is essentially just an address that points to a specific state of the app. I don't think it's necessarily productive to think of the two ways of navigating as separate; they're just two sides of the same coin. There should be no URL that isn't handled by a state. Any URL that doesn't match a state should be caught by the otherwise definition on the $stateProvider and probably redirect to your home page or a 404 page.
In your example, the thing/:thingId/subthing/:subthingId url should map to a predefined state just like any other state. Let's say that state is main.subthing. The process for loading the data, initiating the controller and rendering the UI should be exactly the same whether you get there by calling $state.go('main.subthing', {thing: 123, subthing: 456}) or ui-sref='main.subthing({thing: 123, subthing: 456})' or you paste myapp.com/thing/123/subthing/456 into the browser. They'll all end up at exactly the same place with exactly the same data by calling the exact same logic (most likely loading thing 123 and subthing 456 in resolves and injecting those into a controller).
You're right that if a url can't be handled by the application, that's a sign that something is wrong. Like I said, bad urls should be handled by defining otherwise when setting up states. But pasting a URL into a browser shouldn't require any extra work if your states are defined correctly. URL handling is baked into UI-Router by default.
I partially agree with you. The point where I disagree is when the URL is obtained by a user who is not supposed to access it. Quite often some websites implement authorization code to check if the user who is currently accessing the page is authorized to do so.
Also I think the route could be a bit different. Something like:
{thingid}/{mysubthingid}
This, I think is a cleaner URL and the parameters could be handled in the same way as the one in your example.
I suggest this in order to make it a bit difficult to unauthorized users.
Anyway, it definitely depends on the kind of application you are implementing. If the app's requirement is to be able to access the page by pasting the URL in the browser then I guess your approach is much better.
So I am using a web app which has an iframe pointing to my angularjs application. If I navigate around the parent web app, I notice that the browser history is doubled up. For example, if I navigate to contacts > about us > home, then the history will be:
- home
- about us
- about us
- contacts
- contacts
If I point the iframe to a simple web page with no angular, then the parent app history works fine. I came across this bug on the angularjs website with a similar issue which appears to have been resolved in an earlier version of angular: https://github.com/angular/angular.js/issues/1054 but it doesn't seem to be resolved for some people. I'm using v1.2.26. Has anyone else experienced this issue?
So it turns out my assumptions about routing as the potential cause were correct, and I can't believe I didn't realize this sooner. Essentially my iframe app routes to different locations through some sort of event. Each route, while it does not impact the parent url, will add a new entry to the browser history (which is normal browser behaviour). The iframe app is designed upon page load to run through a couple routes, thus adding to the browser history, and making it appear like each parent page navigation was doubled.
The question now becomes, is there a way to prevent each route execution from adding the browser history?
UPDATE:
$location.replace() seems to be the only solution I can find. The app makes use of $location.path() a lot to trigger a new route; so I've modified it to be $location.path().replace(); This will replace the current history entry instead of adding a new one.