37

I've been looking at these pages (1, 2, 3). I basically want to change my $state, but I don't want the page to reload.

I am currently in the page /schedules/2/4/2014, and I want to go into edit mode when I click a button and have the URL become /schedules/2/4/2014/edit.

My edit state is simply $scope.isEdit = true, so there is no point of reloading the whole page. However, I do want the $state and/or url to change so that if the user refreshses the page, it starts in the edit mode.

What can I do?

Community
  • 1
  • 1
Kousha
  • 32,871
  • 51
  • 172
  • 296

3 Answers3

24

For this problem, you can just create a child state that has neither templateUrl nor controller, and advance between states normally:

// UPDATED
$stateProvider
    .state('schedules', {
        url: "/schedules/:day/:month/:year",
        templateUrl: 'schedules.html',
        abstract: true, // make this abstract
        controller: function($scope, $state, $stateParams) {
            $scope.schedDate = moment($stateParams.year + '-' + 
                                      $stateParams.month + '-' + 
                                      $stateParams.day);
            $scope.isEdit = false;

            $scope.gotoEdit = function() {
                $scope.isEdit = true;
                $state.go('schedules.edit');
            };

            $scope.gotoView = function() {
                $scope.isEdit = false;
                $state.go('schedules.view');
            };
        },
        resolve: {...}
    })
    .state('schedules.view', { // added view mode
        url: "/view"
    })
    .state('schedules.edit', { // both children share controller above
        url: "/edit"
    });

An important concept here is that, in ui-router, when the application is in a particular state—when a state is "active"—all of its ancestor states are implicitly active as well.

So, in this case,

  • when your application advances from view mode to edit mode, its parent state schedules (along with its templateUrl, controller and even resolve) will still be retained.
  • since ancestor states are implicitly activated, even if the child state is being refreshed (or loaded directly from a bookmark), the page will still render correctly.
b0nyb0y
  • 1,436
  • 1
  • 14
  • 14
  • 1
    This would work, except that I already have an abstract state `schedule`, and then two children of it: `schedule.view`, and `schedule.edit`. I need this hierarchy. What you are suggesting would then mean I need to have `schedule`, `schedule.view`, and `schedule.view.edit` which (for reasons I that will take a while), I cannot have – Kousha Jul 08 '14 at 01:10
  • @Kousha That's almost the same as above code: just change `schedules` above to `abstract`, keep `controller` there (so it can be used on both `.view` and `.edit` states), add a `view` state definition similar to `edit` one above, then navigate to `schedules.view` inside `gotoView()`. What could be a problem? – b0nyb0y Jul 08 '14 at 03:19
  • @b0nyb0y how to do if the user surf directly into `/edit` and set `$scope.isEdit to true`? – vzhen Aug 17 '14 at 12:58
  • @vzhen, for that, just check the URL/state (i.e. `$state.is('YOUR_STATE)`) and if the state is in `edit` mode, then set the `scope.isEdit = true`. Read here for more information: http://angular-ui.github.io/ui-router/site/#/api/ui.router.state.$state#methods_is – Kousha Aug 18 '14 at 01:21
24

REF: https://github.com/angular-ui/ui-router/wiki/Quick-Reference#statetransitiontoto-toparams--options

$state.transitionTo('yourState', params, {notify: false});
Tu Duong
  • 401
  • 4
  • 5
0

Adding my answer because I think it's different enough from the accepted answer and may be useful to others:

I had two states, begin and view, with a bunch of optional parameters being synced with the URL for view, like so:

$stateProvider
    .state('begin',
        {
            url: '/',
            template: '<app-element></app-element>'
        })
    .state('view',
        {
            url: '/View?param1&param2&...&paramN',
            template: '<app-element></app-element>'
            params: {
               param1: {
                   value: null,
                   squash: true
               },
               ...
            }
        });

The link function for <app-element> would run any time I tried to sync the parameters using $state.go. Using {notify: false, reload: false} did not work for me. The link function still ran each time. I'm on 0.2 so dynamic isn't an available param option, either. I followed @b0nyb0y's suggestion and turned it into a parent/child relationship, which worked:

$stateProvider
    .state('app',
        {
            url: '/',
            template: '<app-element></app-element>'
        })
    .state('app.view',
        {
            url: 'View?param1&param2&...&paramN',
            params: {
               param1: {
                   value: null,
                   squash: true
               },
               ...
            }
        });
R. Salisbury
  • 1,954
  • 16
  • 17