80

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.

Dehli
  • 5,950
  • 5
  • 29
  • 44
Ian Muir
  • 911
  • 1
  • 9
  • 10
  • 1
    If you want your data to survive route changes on the client side, why not push it into a service? – Riko Apr 30 '14 at 14:19

9 Answers9

133

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:

<a href="#/dashboard/1?maximizeWidgetId=1">Maximum This Widget</a>
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
});
codef0rmer
  • 10,284
  • 9
  • 53
  • 76
  • 1
    If it will be accepted answer I will create separate question without any ":param" things. Just plain stupid question: how to not re-render view. – OZ_ Aug 31 '13 at 18:34
  • 2
    I think [angular-ui-router](http://angular-ui.github.io/ui-router/sample/#/contacts) will be an obvious choice in that case. – codef0rmer Aug 31 '13 at 18:42
  • 1
    I'm using the `$routeUpdate` event. I think the `$routeChangeStart` gets triggered when moving to another location. – lucassp Aug 31 '13 at 20:37
  • @kuchumovn: Unfortunately we are still in 2014. Angular 2.0 will have a revamped routing (probably angular-ui-router built-in) which would do what you intended. – codef0rmer Apr 24 '14 at 18:42
  • 1
    @codef0rmer, I have found this answer more than once. Thanks! Great answer, still after all this time! – frosty Jul 21 '15 at 05:19
  • Adding question mark programatically will cause the URL to have a %3F instead, so I suggest using: $location.path('/store/items').search({'maximizeWidgetId', 1}) Though, this still did not work for me, so angular-ui-router, here I come. – Lajos Mészáros Aug 20 '15 at 10:38
9

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

Community
  • 1
  • 1
bdavidxyz
  • 2,492
  • 1
  • 20
  • 40
  • That looks like it is a duplicate. Unfortunately it looks like they have the same issue, as long as it's part of the url path, not a query variable, it refreshes. – Ian Muir Aug 20 '13 at 18:27
  • No, it's not duplicate. – OZ_ Aug 31 '13 at 17:22
  • It's not a duplicate, since the linked question is about not reloading controller and this is about not reloading the template. The solution in the linked question will make the view reload. – Lajos Mészáros Aug 20 '15 at 10:30
6

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');
Daniel Garmoshka
  • 5,849
  • 39
  • 40
4

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:

  1. Use ui-router instead of ngRoute
  2. 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!

ohanhi
  • 186
  • 2
3

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.

DreamSonic
  • 1,454
  • 11
  • 19
  • 2
    Have you tried using `$state.transitionTo()` instead of `$location.path()`? – DreamSonic Aug 31 '13 at 19:44
  • 1
    I don't change $location.path() at all, where should write `$state.transitionTo()` then? – OZ_ Aug 31 '13 at 19:48
  • Quote: "When a user maximizes a widget, we update the url using location.path, but this is causing the view to re-render." Instead of changing the path, you should call `$state.transitionTo()` which in turn will update the url hash. – DreamSonic Aug 31 '13 at 19:53
  • The idea is that you should have two nested states: one for your dashboard, and one for your maximized widget. When you navigate from parent state to the child state, only the child view will re-render, not the parent view. – DreamSonic Aug 31 '13 at 20:06
2

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).

  1. turn off $routeProvider
  2. 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>
    
  3. 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).

OZ_
  • 12,492
  • 7
  • 50
  • 68
0

If I understood your question right, you want to,

  1. Maximize the widget when the user is on /dashboard/:dashboardId and he maximizes the widget.
  2. 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.

Sivakumar Kailasam
  • 452
  • 3
  • 6
  • 20
  • 1
    Not only $location will refresh the view. ng-include will also, even without changing $location. – OZ_ Aug 31 '13 at 19:46
  • @OZ_ I didn't suggest ngInclude since this is just a state change inside a view I'm merely suggesting probably something like ng-if which would be associated to a function which can figure out routeParams – Sivakumar Kailasam Aug 31 '13 at 20:00
0

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.

Keegan 82
  • 394
  • 2
  • 11
-4

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.

abc
  • 1