I have an AngularJS web app. When a user gets to my domain root URL root-domain/ my app checks automatically his navigator language and if different than english it should automatically include the language in the url, like root-domain/es. This makes my controller reloads and I would like to avoid this for performance and user experience purposes.
I have read the solution provided Can you change a path without reloading the controller in AngularJS? by #Vigrond but for some reason I get next error:
TypeError: $rootScope.$on is not a function
at LocationUrl.$location.path (app.js:140)
at Constructor.HomeCtrl (home.js:15)
in line: var un = $rootScope.$on('$locationChangeSuccess', function () {
According to the approach, you don´t have to do anything else, but I guess I´m missing something.
With the use of Angular UI Router - you can access parameters url using $stateParams. Additionally, the $location service also has a way to access URL parameters: $location.search().param1
I know that sometimes there is an advantage in using $location.search() to retrieve values in the URL.
For example, when changing the URL without reloading state. Example snippet:
$state.current.reloadOnSearch = false;
$location.search('param1', 'new value').replace();
$timeout(function () {
$state.current.reloadOnSearch = undefined;
});
In this case, the URL gets changed with new parameter param1 - if I try to use $stateParams.param1 - its undefined. Meaning, $stateParams doesn't know about that change. In such a scenario, I use $location.search().param1 to get the updated value.
So as you can see, sometimes $location.search() has an advantage over $stateParams.
What are the disadvantages? Can't I just always use $location.search() and not use $stateParams? Is there any advantage in using $stateParams? What situations?
Im trying to get some paramters from the urlcallback coming from an external authentication, in my angular projct using angular-route/$routeProvider
the api redirects to:
http://localhost:8080/dist/?somevar=someval&val2=someotherval#/
Note the params come before #/
I try to read the get values with $routesparams like:"
$scope.$on('$routeChangeSuccess', function() {
// $routeParams should be populated here
console.log($routeParams.somevar);
console.log($routeParams);
});
This returns an emptyresult. however it i change the url to:
http://localhost:8080/dist/#/?somevar=someval&val2=someotherval
it works, with the api I can give a callback url and I set it like:
?callbackUrl=http://localhost:8080/dist/#/
How should I get these parameters from the url whithout changing the callback url?
In this case, the problem is with the Hashbang mode, which has a hashPrefix #. In the configuration phase, you have to enable html5Mode $locationProvider.html5Mode(true);
But this requires URL rewriting on server side and the HTML <base> tag.
Read more here: https://docs.angularjs.org/guide/$location
So the explanation why it does not work for you this way, is when you give the http://localhost:8080/dist/?somevar=someval&val2=someotherval#/ url to angular, it looks for the parameters after the #, and when you populate the $routeParams, it will create the parameters after the #.
I'm trying to demonstrate something using url-query parameters using plunker, but I can't even get the parameters to show up (and then consequently not demonstrate my original issue).
I have created a simple plunker where the states url property looks like this: url: '/root?firstParam'
What I want then is to populate $stateParams.firstParam with whatever I write in the url of the browser for that queryParameter.
Plunker, plunkerWithParameter?firstParam=foo
I would imagine that $stateParams.firstParam would be undefined for the first url, but set to "foo" for the second. But it's undefined in both cases.
How can I get the $stateParams.firstParam to be set?
Edit: It seems that it is actually possible (#Rogerio Soares answer proves this). Keeping my answer here because there was an other error in the code.
There is no way to get the parameters from the browsers search bar in a Plnkr app since it is running in an iFrame. I fixed another problem of yours below:
I added the parameter to your otherwise statement like this:
$urlRouterProvider.otherwise('/root?firstParam=test');
The problem is that you redirected to root when no other route was found, and you didn't specify any parameters to the route.
Updated PlunkR: https://plnkr.co/edit/OxEGVkhzBO332ym3q8fV?p=preview
my two cents : As #Johannes Ferner said Plunker use iframe, but you can at least get your url with document.referrer
It is a little hacky but it work :D
$scope.parameters = [];
var url = document.referrer;
var props = url.split("?")[1]
.split("&")
.map(function(o){
return o.split("=");
});
console.log(props);
for(var i = 0; i < props.length; i++){
var prop = props[i];
console.log(prop);
if($stateParams.hasOwnProperty(prop[0])){
$scope.parameters.push({name: prop[0], value:prop[1]});
}
}
https://plnkr.co/edit/Cejgj1cLJnwQT2LzjsRm?firstParam=foo&p=preview
the Problem is that the plnkr is running within iFrames, so the URL you tried is simply wrong.
Try this one:
https://plnkr.co/edit/jV2pdV6q8g8QZ132Qu3A
I have added a Link to your root state with the first-parameter set to 'Yeah' like this:
<a ui-sref="root({firstParam: 'YEAH'})">Go to Root with Firstparam = "YEAH"</a>
Which results to this URL: https://run.plnkr.co/roo/root?firstParam=YEAH.
Wrong: If you open this URL directly in the browser, the query-params are correctly passed to your AngularJS Application and set to $stateParams.firstParam
Correct: I did not found a way to have a permanent URL to the plnkr without the code being embedded in an iframe.
My single page application loads a home page and I want to display a series of ideas. Each of the ideas is displayed in an animated flash container, with animations displayed to cycle between the ideas.
Ideas are loaded using $http:
$scope.flash = new FlashInterface scope:$scope,location:$location
$http.get("/competition.json")
.success (data) ->
$scope.flash._init data
However, to benefit from history navigation and UX I wish to update the address bar to display the correct url for each idea using $location:
$location.path "/i/#{idea.code}"
$scope.$apply()
I am calling $apply here because this event comes from outwith the AngularJS context ie Flash. I would like for the current controller/view to remain and for the view to not reload. This is very bad because reloading the view results in the whole flash object being thrown away and the preloader cycle beginning again.
I've tried listening for $routeChangeStart to do a preventDefault:
$scope.$on "$routeChangeStart", (ev,next,current) ->
ev.preventDefault()
$scope.$on "$routeChangeSuccess", (ev,current) ->
ev.preventDefault()
but to no avail. The whole thing would be hunky dory if I could figure out a way of overriding the view reload when I change the $location.path.
I'm still very much feeling my way around AngularJS so I'd be glad of any pointers on how to structure the app to achieve my goal!
Instead of updating the path, just update query param with a page number.
set your route to ignore query param changes:
....
$routeProvider.when('/foo', {..., reloadOnSearch: false})
....
and in your app update $location with:
...
$location.search('page', pageNumber);
...
From this blog post:
by default all location changes go through the routing process, which
updates the angular view.
There’s a simple way to short-circuit this, however. Angular watches
for a location change (whether it’s accomplished through typing in the
location bar, clicking a link or setting the location through
$location.path()). When it senses this change, it broadcasts an
event, $locationChangeSuccess, and begins the routing process. What
we do is capture the event and reset the route to what it was
previously.
function MyCtrl($route, $scope) {
var lastRoute = $route.current;
$scope.$on('$locationChangeSuccess', function(event) {
$route.current = lastRoute;
});
}
My solution was to use the $routeChangeStart because that gives you the "next" and "last" routes, you can compare them without the need of an extra variable like on $locationChangeSuccess.
The benefit is being able to access the "params" property on both "next" and "last" routes like next.params.yourproperty when you are using the "/property/value" URL style and of course use $location.url or $location.path to change the URL instead of $location.search() that depends on "?property=value" URL style.
In my case I used it not only for that but also to prevent the route to change is the controller did not change:
$scope.$on('$routeChangeStart',function(e,next,last){
if(next.$$route.controller === last.$$route.controller){
e.preventDefault();
$route.current = last.$$route;
//do whatever you want in here!
}
});
Personally I feel like AngularJS should provide a way to control it, right now they assume that whenever you change the browser's location you want to change the route.
You should be loading $location via Dependency Injection and using the following:
$scope.apply(function () {
$location.path("yourPath");
}
Keep in mind that you should not use hashtags(#) while using $location.path. This is for compability for HTML5 mode.
The $locationChangeSuccess event is a bit of a brute force approach, but I found that checking the path allows us to avoid page reloads when the route path template is unchanged, but reloads the page when switching to a different route template:
var lastRoute = $route.current;
$scope.$on('$locationChangeSuccess', function (event) {
if (lastRoute.$$route.originalPath === $route.current.$$route.originalPath) {
$route.current = lastRoute;
}
});
Adding that code to a particular controller makes the reloading more intelligent.
Edit: While this makes it a bit easier, I ultimately didn't like the complexity of the code I was writing to keep friendly looking URL's. In the end, I just switched to a search parameter and angular handles it much better.
I needed to do this but after fussing around trying to get the $locationChange~ events to get it to work I learned that you can actually do this on the route using resolve.
$routeProvider.when(
'/page',
{
templateUrl : 'partial.html',
controller : 'PageCtrl',
resolve : {
load : ['$q', function($q) {
var defer = $q.defer();
if (/*you only changed the idea thingo*/)
//dont reload the view
defer.reject('');
//otherwise, load the view
else
defer.resolve();
return defer.promise;
}]
}
}
);
With AngularJS V1.7.1, $route adds support for the reloadOnUrl configuration option.
If route /foo/:id has reloadOnUrl = false set, then moving from /foo/id1 to /foo/id2 only broadcasts a $routeUpdate event, and does not reload the view and re-instantiate the controller.