The context:
It is necessary for me to fetch the URL from the server when the user clicks the link. It is not available beforehand. The click on the link should work as normal, but with that fetched URL.
The procedure is as follows:
href="#"
before it is clickedng-mousedown
or ng-click
, and fetch the URL from the server using a $http
request.#
placeholder).The problem comes on the last step. Since the $http
request is asynchronous, I suspect there is a timing issue. My suspicion is: If the server request is fast enough, it will go through, and the URL is changed before the original click goes through. If not, then the original click goes through and tries to go to the # placeholder URL. So nothing will happen when clicking the link.
I want to make the original click wait for the result of the $http
request to have returned with the URL. The reason the original click is important is that it could either be a left or a middle mouse button click (cmd+click), and I don't know which one, so it'd be hard to call it myself from the javascript.
So, any clever ideas on how can make the original click go through with the fetched URL?
If you really can't resolve the link before a click is detected, then you would be better off using an ngClick directive to call a function which might show a loading screen while waiting for your $http promise to resolve and then use $location.path() to actually initiate the route change. For example:
The HTML
<button ng-click="changeRoute($event)">Click me</button>
And the code
angular.module('myApp', [])
.controller('pageCtrl', function($scope, $http, $location, $window) {
$scope.changeRoute = function(event) {
console.log('Fetching URL');
// The popup must be created before the callback, because the callback
// is not considered user-initiated by the browser. Non-user-initiated
// events always open in a new browser window (not a tab).
if (event.which === 2 || (event.which ===1 && (event.metaKey || event.ctrlKey))) {
// MIDDLE CLICK or CMD+LEFTCLICK - new tab
var popup = $window.open("about:blank", "_blank");
// the about:blank is to please Chrome, and _blank to please Firefox
}
$http.get('/some/path')
.success(function(data){
//check that it's a good URL, then route to it
//$location.path(data);
if (event.which === 1 && !event.metaKey && !event.ctrlKey) {
// LEFTCLICK - same window
$window.location.href = data;
} else if (event.which === 2 || (event.which ===1 && (event.metaKey || event.ctrlKey))) {
// MIDDLE CLICK or CMD+LEFTCLICK - new tab
popup.location = data;
}
})
.error(function(data, status, headers, config) {
console.error('Failed to get URL: ' + status);
});
console.log('Fetch request sent, awaiting response...');
};
});
Updated to support external links and middle click will now open in a new window
Updated to support:
You should do event.preventDefault()
first to make time for $http
to complete its process. To do that in Angular, you should pass $event
to the ng-click
handler.
Example HTML:
<body ng-controller="appController">
<a href="#" ng-click="navigate($event)">LINK</a>
</body>
App:
var app = angular.module('app', []);
app.controller('appController',
function ($scope, $http, $window, $log) {
// generic function for window navigation
function nav(url, newTab) {
if (newTab) {
$window.open(url, '_blank');
} else {
$window.location.href = url;
}
}
// handler for ng-click directive
$scope.navigate = function (event) {
// This is the key -> preventing default navigation
event.preventDefault();
// determine whether to open a new page or not
var newTab = ((event.which === 1 && (event.metaKey || event.ctrlKey)) || event.which === 2);
// get the URL from a file
$http.get('links.json')
.success(function (data, status, headers, config) {
// navigate when URL is fetched
nav(data.someLink, newTab);
})
.error(function (data, status, headers, config) {
$log.debug('Error:', data, status);
});
};
});
See an example of the exposed $event
object here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With