简体   繁体   中英

Ui-router loads state, then how can I run a function?

I'm new to Angular and states and wrapping my head around ui-router. I've been doing it with jQuery for too long. In jQuery, I can load up something with ajax, then on the success, perhaps run another function. How do you do that with Angular?

For example, I have the following

var ivApp = angular.module('ivApp', ['ui.router']);

ivApp.config(function($urlRouterProvider, $stateProvider){

  $urlRouterProvider.otherwise('/');

  $stateProvider
    .state('home', {
      url: '/',
      templateUrl: 'partials/partial-home.html'
    })

 });

Which simply loads up partial-home.html into my ui-view . But how to tell it to run a function once that is done? For example, I have authenticate.js and a function authenticate() . How do I run authenticate() once 'home' state has loaded?

Additionally, can I tell angular to only load authenticate.js for this state? Or should I have already loaded it in the template. I know that if I include the script in partial-home.html (eg <script src="authenticate.js"></script> ) chrome throws me an error about synchronous xmlhttprest being deprecated. So somhow in the config, can I declare authenticat.js as a dependency of the state or something like that?

At the moment I have worked out I can do something like:

ivApp.controller('authenticate', function($scope) {

  // start authorisation
  authenticate();

});

And then define the controller authenticate in my ui-router states. But is that how it's done? It works basically. My authenticate function is doing things like changing things in the DOM, but I read controllers shouldn't be used for this.

Thanks for any pointers

Let's break down into parts.

If you just want to load authenticate.js in this particular home state, use ocLazyLoad . It's one of the best way to load a resource lazily. And it works really well if ui-router too!

    $stateProvider.state('index', {
      url: "/", // root route
      views: {
        "lazyLoadView": {
          controller: 'AppCtrl', // This view will use AppCtrl loaded below in the resolve
          templateUrl: 'partial-home.html'
        }
      },
      resolve: { // Any property in resolve should return a promise and is executed before the view is loaded
        loadMyCtrl: ['$ocLazyLoad', function($ocLazyLoad) {
          // you can lazy load files for an existing module
                 return $ocLazyLoad.load('js/authenticate.js');
        }]
      }
    });

If you want to run authenticate() once the state is loaded, there are quite a number of ways to do it. One way of course is listening to the $stateChangeSuccess event, but I would avoid using it since you know, global variables, and global variables are bad. I do not want to pollute my $rootScope just because I have a really specific use case.

You can use resolve in ui-router too. Resolve is executed after the state is loaded and before the controller is instantiated. I would recommend to use this method as you can chain your promises together with ocLazyLoad , if you are using it (which you should).

Manipulating DOMs after a state is loaded? Sure, that's what templateUrl for! Design your template such that it accomadates to your authenticate() functions. If you combine it with resolve , there isn't really much of a problem separating concerns as you would already have executed authenticate() before controller is loaded.

Edit: Adding in Plnkr

You want to first lazily-load authenticate.js , and then use the function inside authenticate.js to do something. Since resolve in ui.router executes promise chains in parallel, we have to chain them up, ie, load your jsfiles first, and then return your status of authentication.

We need to declare a deferred promise using $q service. We then return this promise in the resolve, so that you controller is listening to one promise instead of two. Here is how:

 $stateProvider
  .state('Home', {
    templateUrl: 'home.html',
    controller: 'homeCtrl',
    resolve: {
      //need to chain our promises since we neeed to first load the authenticate.js
      //and second, execute authenticate()
      loadJsAndAuth: ['$ocLazyLoad', '$q', '$injector', function($ocLazyLoad, $q, $injector) {
        //declare a deferred promise
        var deferred = $q.defer();

        //now load the authenticate.js
        $ocLazyLoad.load('authenticate.js').then(
          //load successful! proceed to use our authenticate function!
          function(success) {
            //since we already have loaded authenticatejs, now we can inject the service and use it
            var authSvc = $injector.get('authenticateSvc');

            //this is just a demo on how to authenticate. 
            //change this to banana to see the authenticate fail
            var fruits = 'apple'

            if (authSvc.authenticate(fruits)) {
              //authenticate pass, resolve the promise!
              deferred.resolve('authenticated!');
            }
            //authenticate fail, reject the promise
            deferred.reject('authenticate failed');

          },
          //load of jsfiles failed! reject the promise.
          function(error) {
            deferred.reject('Cannot load authenticate.js')
          })

        return deferred.promise;
      }]
    }
  })

And in your controller, you can get the resolved promises!

//you can get access to what is is being resolved by loadJsAndAuth
.controller('homeCtrl', ['$scope', 'loadJsAndAuth', function($scope, loadJsAndAuth) {

  $scope.status = loadJsAndAuth // this is resolved promises.

}]);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM