Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the Angular ui-router lifecycle? (for debugging silent errors)

The best I've found is http://www.ng-newsletter.com/posts/angular-ui-router.html. It doesn't go as deep as, for example, the order in which $stateChangeStart, exampleState.onEnter, exampleState.resolve, and exampleState.templateProvider fire.

A great answer format would be clean. Something like:

  1. Initial pageload of state foo:

    1. Angular lifecycle step 1
    2. UI router lifecycle step 1
    3. UI router lifecycle resolves occur
    4. UI router lifecycle onEnter fires
    5. Angular lifecycle step 2
  2. State change foo -> bar

    1. $stateChangeStart event fires
    2. foo onExit fires
    3. bar onEnter Fires
    4. templateUrl gets the template
    5. UI router plugs back into the Angular lifecycle in the digest loop (or wherever).
  3. Nested states

  4. Multiple named views:

  5. ui-sref clicked

Etc... Thanks!

EDIT: Debugging functions provided enough insight to meet the need. See my answer below for a snippet.

like image 562
Adam Avatar asked Dec 23 '13 14:12

Adam


5 Answers

After some experimenting, I figured out how to see into the lifecycle well enough to debug my app and get a feel for what was happening. Using all the events, including onEnter, onExit, stateChangeSuccess, viewContentLoaded from here, gave me a decent picture of when things are happening in a way that's more both more flexible and specific to my code than a written out lifecycle. In the app module's "run" function, I placed:

This code would have saved me days of time and confusion if I started using it when I first started with Angular and UI-router. UI-router needs a "debug" mode that enables this by default.

$rootScope.$on('$stateChangeStart',function(event, toState, toParams, fromState, fromParams){
  console.log('$stateChangeStart to '+toState.name+'- fired when the transition begins. toState,toParams : \n',toState, toParams);
});
$rootScope.$on('$stateChangeError',function(event, toState, toParams, fromState, fromParams, error){
  console.log('$stateChangeError - fired when an error occurs during transition.');
  console.log(arguments);
});
$rootScope.$on('$stateChangeSuccess',function(event, toState, toParams, fromState, fromParams){
  console.log('$stateChangeSuccess to '+toState.name+'- fired once the state transition is complete.');
});
$rootScope.$on('$viewContentLoading',function(event, viewConfig){
   console.log('$viewContentLoading - view begins loading - dom not rendered',viewConfig);
});

/* $rootScope.$on('$viewContentLoaded',function(event){
     // runs on individual scopes, so putting it in "run" doesn't work.
     console.log('$viewContentLoaded - fired after dom rendered',event);
   }); */

$rootScope.$on('$stateNotFound',function(event, unfoundState, fromState, fromParams){
  console.log('$stateNotFound '+unfoundState.to+'  - fired when a state cannot be found by its name.');
  console.log(unfoundState, fromState, fromParams);
});
like image 139
Adam Avatar answered Nov 08 '22 23:11

Adam


I took @Adam's solution and wrap it into a service so I can switch debugging (printing to console) on and off from within my application.

In the template:

<button ng-click="debugger.toggle()">{{debugger.active}}</button>

In the controller:

function($scope, PrintToConsole){ $scope.debugger = PrintToConsole; }

Or to just switch it on:

angular.module('MyModule', ['ConsoleLogger'])
.run(['PrintToConsole', function(PrintToConsole) {
    PrintToConsole.active = true;
}]);

The service:

angular.module("ConsoleLogger", [])
.factory("PrintToConsole", ["$rootScope", function ($rootScope) {
    var handler = { active: false };
    handler.toggle = function () { handler.active = !handler.active; };
    $rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
        if (handler.active) {
            console.log("$stateChangeStart --- event, toState, toParams, fromState, fromParams");
            console.log(arguments);
        };
    });
    $rootScope.$on('$stateChangeError', function (event, toState, toParams, fromState, fromParams, error) {
        if (handler.active) {
            console.log("$stateChangeError --- event, toState, toParams, fromState, fromParams, error");
            console.log(arguments);
        };
    });
    $rootScope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState, fromParams) {
        if (handler.active) {
            console.log("$stateChangeSuccess --- event, toState, toParams, fromState, fromParams");
            console.log(arguments);
        };
    });
    $rootScope.$on('$viewContentLoading', function (event, viewConfig) {
        if (handler.active) {
            console.log("$viewContentLoading --- event, viewConfig");
            console.log(arguments);
        };
    });
    $rootScope.$on('$viewContentLoaded', function (event) {
        if (handler.active) {
            console.log("$viewContentLoaded --- event");
            console.log(arguments);
        };
    });
    $rootScope.$on('$stateNotFound', function (event, unfoundState, fromState, fromParams) {
        if (handler.active) {
            console.log("$stateNotFound --- event, unfoundState, fromState, fromParams");
            console.log(arguments);
        };
    });
    return handler;
}]);
like image 44
Endy Tjahjono Avatar answered Nov 08 '22 23:11

Endy Tjahjono


How ui-router manages urls beside the default $location provider is not clear so, adding more debugging code here. Hopefully it'll be helpful. These are from https://github.com/ryangasparini-wf/angular-website-routes

$scope.$on('$routeChangeError', function(current, previous, rejection) {
    console.log("routeChangeError", currrent, previous, rejection);
});

$scope.$on('$routeChangeStart', function(next, current) {
    console.log("routeChangeStart");
    console.dir(next);
    console.dir(current);
});

$scope.$on('$routeChangeSuccess', function(current, previous) {
    console.log("routeChangeSuccess");
    console.dir(current);
    console.dir(previous);
});

$scope.$on('$routeUpdate', function(rootScope) {
    console.log("routeUpdate", rootScope);
});
like image 2
s6712 Avatar answered Nov 08 '22 23:11

s6712


I needed to debug the ui-router I was using along with the ui-router-extras sticky state package. I found that sticky states has builtin debugging that helped solve my problem. It logs information about the state transitions and which are inactive/active.

https://christopherthielen.github.io/ui-router-extras/#/sticky

angular.module('<module-name>').config(function ($stickyStateProvider) {
    $stickyStateProvider.enableDebug(true);
});
like image 2
Zack Huston Avatar answered Nov 09 '22 00:11

Zack Huston


UI Router has been updated with Transition Hooks.

Use $transition$ service to access onError hook and catch the error.

List of hook :

  • onBefore - The transition is about to start
  • onStart - The transition has started
  • onExit - (state events) Any exiting states are exited
  • onRetain - (state events) Any retained states are retained
  • onEnter - (state events) Any entering states are entered
  • onFinish - The transition is about to finish
  • onSuccess - he transition is finished and is either successful or errored.
  • onError - he transition is finished and is either successful or errored.

You can checkout the overview to learn about Transition : https://ui-router.github.io/guide/transitions

See the documentation for the Transition Hook events : https://ui-router.github.io/guide/transitionhooks

like image 1
Disfigure Avatar answered Nov 09 '22 00:11

Disfigure