16

Updated question

Question in title still stands - is it possible to catch ?parameters and cancel view reload.

Original question: I need to add anchor support for my application (anchors to topics).

Here's how far I am:

angular.module('app.topics', [
    'ui.state',
    'ui.router',
    'restangular'
    'app.topics.controllers'
])
.config(function config($stateProvider) {
    $stateProvider.state('viewtopic', {
        url: '/topics/:slug?section',
        onEnter: function($stateParams) {
            // Works, as state has been reloaded.
            console.log('$stateParams', $stateParams);
        },
        resolve: {
            topic: function ($stateParams, Topic) {
                // Wrapper around Restangular. Returns promise.
                return new Topic().get($stateParams.slug);
            }
        },
        views: {
            'main': {
                controller: 'TopicCtrl',
                templateUrl: 'topics/templates/details.tpl.html'
            },
            'sidebar': {
                controller: 'SidebarCtrl',
                templateUrl: 'topics/templates/sidebar.tpl.html'
            }
        }
    });
});

angular.module('app.topics.controllers', [])
    .controller('TopicCtrl', function ($scope, topic, $rootScope, $location,
                                         $anchorScroll, $stateParams) {
        $scope.topic = topic;
        $scope.slug = $stateParams.slug;
        $scope.section = $stateParams.section;
        // ..

        $scope.$watch('$stateParams.section', function (newValue, newValue) {
            // Does not work.
            console.log('stateParams.section changed', newValue, newValue);
        });
    });

My root issue is when I click on link with #/topics/slug-name?section=section-1, state is reloaded completely, which in turn re-requests data for Topic. How can one just "refresh" query parameters (and catch that event in controller) but don't reload state (to keep data loaded)?

EDIT

For my issue there is a rather simple solution which I've failed to notice - per AngularJS documentation on "Hashbang and HTML5 Modes" - last hash is accessible via $location.hash() and scrolling can be initiated by $anchorScroll()

So my current solution is:

In controller:

$scope.$watch('$location.hash', function () {
    _.defer($anchorScroll);
});

Defer is needed because content may not be parsed yet and there are no id's.

In template I just had to set target to #/topics/slug-name#section-name.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Kristaps Karlsons
  • 482
  • 1
  • 7
  • 22
  • There are events emitted by uiRouter. For example there is a '$viewContentLoading' event https://github.com/angular-ui/ui-router/wiki#wiki-view-load-events Have you tried catch it and stop before view rendering happened ? – Igor Malyk Jan 31 '14 at 12:28
  • It feels like you have found a solution with `$location.hash()`. What did you expect when you started the bounty? – Khanh TO Jan 31 '14 at 14:31

3 Answers3

20

My two cents

First check @doodeec answer about setting up reloadOnSearch to false:

{
    route:'/',
    resolve: {
        templateUrl:'template.html',
        reloadOnSearch: false
    }
},

Then on the controller you want to catch this:

/* Setup to detect when there is a change in the search */
$scope.$on('$routeUpdate', function(next, current) {
   // Do something when routeupdate is called. 
});

I case you want to change the search:

$location.search({'slide_id': slide_id})

Also take care for a small Gotcha:

If your route params do not change (or change to the same value) since reload is disabled then you might need to force the location change:

$scope.$emit('$locationChangeSuccess');

From comments:

Without ngRoute there will be no $routeUpdate event. in this case you need to listen to $scope.$on('$locationChangeSuccess', ...) instead

Jimmy Kane
  • 16,223
  • 11
  • 86
  • 117
13

Have you tried reloadOnSearch parameter within routes definition?

{
    route:'/',
    resolve: {
        templateUrl:'template.html',
        reloadOnSearch: false
    }
},
doodeec
  • 2,927
  • 19
  • 23
4

This can be achieved by using nested states. the code will look something like this

 $stateProvider.state('viewtopic', {abstract:true,
url:/topics,
resolve: {
            topic: function ($stateParams, Topic) {
                // Wrapper around Restangular. Returns promise.
                return new Topic().get($stateParams.slug);
            }
        },
views: {
templateUrl:<template>
}
});

$stateProvider.state('viewtopic.impl', {abstract:true,
        url: '/:slug?section',
        onEnter: function($stateParams) {
            // Works, as state has been reloaded.
            console.log('$stateParams', $stateParams);
        },

        views: {
            'main': {
                controller: 'TopicCtrl',
                templateUrl: 'topics/templates/details.tpl.html'
            },
            'sidebar': {
                controller: 'SidebarCtrl',
                templateUrl: 'topics/templates/sidebar.tpl.html'
            }
        }

for more read ->https://github.com/angular-ui/ui-router/wiki/Nested-States-%26-Nested-Views

himangshuj
  • 730
  • 5
  • 9