How to prevent Angular from turning URL hash into a path - angularjs

I have an angular app with a number of links. Clicking each link displays a particular set of data. I would like each link to set a hash in the URL, like http://foo.com/bar#item1, and I'd also like to show the page with the particular set of data displaying when that URL with the hash is accessed directly.
I was hoping to do it by reading / manipulating the $location.hash(), but as soon as I inject $location into my controller, the #item1 in the URL changes to #/item1. Is there a way to prevent Angular from doing this, and what's the best way of setting up what I described?

You could pass parameters in the routes /bar/item1, catch it with $routeParams and if exist make $location.hash().
"controller"
var anchor = $routeParams.anchorBottom;
if(anchor){
$location.hash(anchor);
$anchorScroll();
}
"link to section"
$location.path('/heart/bottom');
"route"
.when('/heart/:anchorBottom',{
You could see this running here.

Related

Location search parameters disappear when I load a new template

I am using $location.search(params) to store an array in my url. This array has determines how the page will load. It works well only problem is that I have a set of tabs on my page, when ever I click the the tab a new template is loaded with a new controller and the url variables disappear.
I dont understand this behavior $location.search() is not being called again in the new controller. How can I get the Url to stay static?
In the $routeProvider set reloadOnSearch: false so you don't reload when $location.search() changes.
https://docs.angularjs.org/api/ngRoute/provider/$routeProvider
Change the path using
$location.path('/differentRoute').search(params);
Doing so would change the route and set the search parameters again.

$location.path() vs $location.hash() in angularjs

if my present URL is : xzy.com/#/home/new
$location.hash() gives home/new and $location.path also gives home/new
What is the difference in the two?
If inside the controller of home/new I write $location.hash("#/home/new") or $location.path("/home/new") both do not reload the partial but if I do location.href="#/home/new", it reloads the partial. Why is this?
Also, if inside the partial there is a <a href="#/home/new"> that will also reload the partial.
Why doesn't setting the path/hash reload the partial?
There are two parts to the route.
The first "hash" is really there just for browser compatibility and won't show if you are in HTML5 mode.
For example, given this URL:
http://localhost/spa.htm
If you set:
$location.path('/myPath');
you will get:
http://localhost/spa.htm#/myPath
In this case, the "hash" is just for the browser to hold the URL, the method is path. Note also when you call path without a preceding / it is added, i.e. 'myPath' becomes '/myPath'.
If you subsequently set:
$location.hash('myHash');
You will get:
http://localhost/spa.htm#/myPath#myHash
Finally, let's assume you did not set the path first, then you'll get:
http://locahost/spa.htm#/#myHash
If you are using HTML5 mode, the path is appended without the initial hash.
The first hash is used to append the route, the second is a reference to content on the page. For example, if you use the $anchorScroll service it will respond to what is placed in $location.hash() and not in $location.path().
To summarize:
http://localhost/spa.htm#{path}#{hash}
I had a similar question this morning then Google led me here.
Inspired by other answers and some Googlings I've done, here is my result:
for example, given a browser url:
http://localhost:8080/test.html#!/testpath#testhash%20with%20someothers
in AngularJS ,
url is
/testpath#testhash
path is
/testpath
in another word , from left to right ,path starts at the first character in url and ends at the # or ? or the end of url.
path always start with '/' .so ,if no path is specified ,the path is set as "/" rather than ""
hash is
testhash%20with%20someothers
in another word ,hash starts at the next character of # in url and ends at the end of url
location.href is not implemented in AngularJS. when you say: location.href="#",it behaves like clicking an anchor tag :
click
when invoke the method $location.path,$location.hash as setters, they do change the browser url to match your demands.
And ,why you want AngularJS to RELOAD a page at all? :)
The reason for the second part of your question, why it is not redirecting might be:
You might need to update the binding, with $scope.$apply , this is required when you are using the code other than angular like native javascript, jquery code
for example:
$scope.$apply(function(){
$location.path("#/home/new");
})

AngularJS redirect without pushing a history state

I'm working on an AngularJS app that has a catch all route (eg, .when('/:slug', {...)) which is necessary to support legacy url formats from a previous (non-angular) version of the app. The controller that responds to the catch all tries pulling a related object, and, if not found, redirects to a 404 page using the $location.path method. This works at getting the user to the 404 page, but when the user hits back in their browser it takes them back to the page that forced them to the 404 page in the first place and they end up being unable to escape the cycle.
My question is if there is 1) a better pattern for handling this situation, or 2) if there is a way to reroute the user that doesn't force a history push state in the browser?
You can change the url without adding to the history state, found here under "Replace Method". This is effectively the same as calling HTML5's history.replaceState().
$location.path('/someNewPath').replace();
I haven't found that it's possible to change the view without changing the url. The only method to change the view, that I've found, is to change the location path.
The normal operation of the route system is for the $route service to watch for the $locationChangeSuccess event and then begin loading a route. When it's done loading the template, performing the resolve steps and instantiating the controller it then in turn broadcasts a $routeChangeSuccess event. That $routeChangeSuccess is monitored by the ng-view directive, and that's how it knows to swap out the templates and scopes once the new route is ready.
With all of the above said, it may work to have application code emulate the behavior of the $route service by updating the current route and emitting the route change event to get the view to update:
var errorRoute = $route.routes[null]; // assuming the "otherwise" route is the 404
// a route instance is an object that inherits from the route and adds
// new properties representing the routeParams and locals.
var errorRouteInstance = angular.inherit(
errorRoute,
{
params: {},
pathParams: {},
}
);
// The $route service depends on this being set so it can recover the route
// for a given route instance.
errorRouteInstance.$$route = errorRoute;
var last = $route.current;
$route.current = errorRouteInstance;
// the ng-view code doesn't actually care about the parameters here,
// since it goes straight to $route.current, but we should include
// them anyway since other code might be listening for this event
// and depending on these params to behave as documented.
$rootScope.broadcast('$routeChangeSuccess', errorRoute, last);
The above assumes that your "otherwise" route doesn't have any "resolve" steps. It also assumes that it doesn't expect any $routeParams, which is of course true for the "otherwise" route but might not be true if you use a different route.
It's unclear what of the above is depending on implementation details vs. interface. The $routeChangeSuccess event is certainly documented, but the $$route property of the route instance seems to be an implementation detail of the route system given its double-dollar-sign name. The detail that the "otherwise" route is kept in the route table with the key null is possibly also an implementation detail. So with all of this said, this behavior may not remain functional in future versions of AngularJS.
For more information you could refer to the ng-view code that handles this event, which is ultimately what the above code is trying to please, along with the event emitting code that I used as the basis for the above example. As you could infer from these links, the information in this post is derived from the latest master branch of AngularJS, which at the time of writing is labelled as 1.2.0-snapshot.

Updating URL in Angular JS without re-rendering view

I'm building a dashboard system in AngularJS and I'm running into an issue with setting the url via $location.path
In our dashboard, we have a bunch of widgets. Each shows a larger maximized view when you click on it. We are trying to setup deep linking to allow users to link to a dashboard with a widget maximized.
Currently, we have 2 routes that look like /dashboard/:dashboardId and /dashboard/:dashboardId/:maximizedWidgetId
When a user maximizes a widget, we update the url using $location.path, but this is causing the view to re-render. Since we have all of the data, we don't want to reload the whole view, we just want to update the URL. Is there a way to set the url without causing the view to re-render?
HTML5Mode is set to true.
In fact, a view will be rendered everytime you change a url. Thats how $routeProvider works in Angular but you can pass maximizeWidgetId as a querystring which does not re-render a view.
App.config(function($routeProvider) {
$routeProvider.when('/dashboard/:dashboardId', {reloadOnSearch: false});
});
When you click a widget to maximize:
Maximum This Widget
or
$location.search('maximizeWidgetId', 1);
The URL in addressbar would change to http://app.com/dashboard/1?maximizeWidgetId=1
You can even watch when search changes in the URL (from one widget to another)
$scope.$on('$routeUpdate', function(scope, next, current) {
// Minimize the current widget and maximize the new one
});
You can set the reloadOnSearch property of $routeProvider to false.
Possible duplicate question : Can you change a path without reloading the controller in AngularJS?
Regards
For those who need change full path() without controllers reload
Here is plugin: https://github.com/anglibs/angular-location-update
Usage:
$location.update_path('/notes/1');
I realize this is an old question, but since it took me a good day and a half to find the answer, so here goes.
You do not need to convert your path into query strings if you use angular-ui-router.
Currently, due to what may be considered as a bug, setting reloadOnSearch: false on a state will result in being able to change the route without reloading the view. The GitHub user lmessinger was even kind enough to provide a demo of it. You can find the link from his comment linked above.
Basically all you need to do is:
Use ui-router instead of ngRoute
In your states, declare the ones you wish with reloadOnSearch: false
In my app, I have an category listing view, from which you can get to another category using a state like this:
$stateProvider.state('articles.list', {
url: '{categorySlug}',
templateUrl: 'partials/article-list.html',
controller: 'ArticleListCtrl',
reloadOnSearch: false
});
That's it. Hope this helps!
We're using Angular UI Router instead of built-in routes for a similar scenario. It doesn't seem to re-instantiate the controller and re-render the entire view.
How I've implemented it:
(my solution mostly for cases when you need to change whole route, not sub-parts)
I have page with menu (menuPage) and data should not be cleaned on navigation (there is a lot of inputs on each page and user will be very very unhappy if data will disappear accidentally).
turn off $routeProvider
in mainPage controller add two divs with custom directive attribute - each directive contains only 'templateUrl' and 'scope: true'
<div ng-show="tab=='tab_name'" data-tab_name-page></div>
mainPage controller contains lines to simulate routing:
if (!$scope.tab && $location.path()) {
$scope.tab = $location.path().substr(1);
}
$scope.setTab = function(tab) {
$scope.tab = tab;
$location.path('/'+tab);
};
That's all. Little bit ugly to have separate directive for each page, but usage of dynamic templateUrl (as function) in directive provokes re-rendering of page (and loosing data of inputs).
If I understood your question right, you want to,
Maximize the widget when the user is on /dashboard/:dashboardId and he maximizes the widget.
You want the user to have the ability to come back to /dashboard/:dashboardId/:maximizedWidgetId and still see the widget maximized.
You can configure only the first route in the routerConfig and use RouteParams to identify if the maximized widget is passed in the params in the controller of this configured route and maximize the one passed as the param. If the user is maximizing it the first time, share the url to this maximized view with the maximizedWidgetId on the UI.
As long as you use $location(which is just a wrapper over native location object) to update the path it will refresh the view.
I have an idea to use
window.history.replaceState('Object', 'Title', '/new-url');
If you do this and a digest cycle happens it will completely mangle things up. However if you set it back to the correct url that angular expects it's ok. So in theory you could store the correct url that angular expects and reset it just before you know a digest fires.
I've not tested this though.
Below code will let you change url without redirection such as: http://localhost/#/691?foo?bar?blabla
for(var i=0;i<=1000;i++) $routeProvider.when('/'+i, {templateUrl: "tabPages/"+i+".html",reloadOnSearch: false});
But when you change to http://localhost/#/692, you will be redirected.

AngularJS - How can I do a redirect with a full page load?

I want to do a redirect that does a full page reload so that the cookies from my web server are refreshed when the page loads. window.location = "/#/Next" and window.location.href = "/#/Next" don't work, they do an Angular route which does not hit the server.
What is the correct way to make a full server request within an Angular controller?
For <a> tags:
You need to stick target="_self" on your <a> tag
There are three cases where AngularJS will perform a full page reload:
Links that contain target element
Example: link
Absolute links that go to a different domain
Example: link
Links starting with '/' that lead to a different base path when base is defined
Example: link
Using javascript:
The $location service allows you to change only the URL; it does not allow you to reload the page. When you need to change the URL and reload the page or navigate to a different page, please use a lower level API: $window.location.href.
See:
https://docs.angularjs.org/guide/$location
https://docs.angularjs.org/api/ng/service/$location
We had the same issue, working from JS code (i.e. not from HTML anchor). This is how we solved that:
If needed, virtually alter current URL through $location service. This might be useful if your destination is just a variation on the current URL, so that you can take advantage of $location helper methods. E.g. we ran $location.search(..., ...) to just change value of a querystring paramater.
Build up the new destination URL, using current $location.url() if needed. In order to work, this new one had to include everything after schema, domain and port. So e.g. if you want to move to:
http://yourdomain.example/YourAppFolder/YourAngularApp/#/YourArea/YourAction?culture=en
then you should set URL as in:
var destinationUrl = '/YourAppFolder/YourAngularApp/#/YourArea/YourAction?culture=en';
(with the leading '/' as well).
Assign new destination URL at low-level: $window.location.href = destinationUrl;
Force reload, still at low-level: $window.location.reload();
After searching and giving hit and trial session I am able to solove it by first specifying url like
$window.location.href = '/#/home/stats';
then reload
$window.location.reload();
I had the same issue. When I use window.location, $window.location or even <a href="..." target="_self"> the route does not refresh the page. So the cached services are used which is not what I want in my app. I resolved it by adding window.location.reload() after window.location to force the page to reload after routing. This method seems to load the page twice though. Might be a dirty trick, but it does the work. This is how I have it now:
$scope.openPage = function (pageName) {
window.location = '#/html/pages/' + pageName;
window.location.reload();
};
Try this
$window.location.href="#page-name";
$window.location.reload();

Resources