3

I'm using ui-router in an app that will have dozens of templates. Each template will have a controller.

From what I've been reading, something like this (to setup a route) ought to work:

.config(function($stateProvider, $urlRouterProvider) {
  $stateProvider
    .state('start', {
      url: '/start',
      templateUrl: 'partials/start.html',
      controller: 'StartCtrl'
    })
});

That's assuming StartCtrl was previously defined. The app will eventually have dozens of controllers, and don't want the overhead of downloading all of them at once. How can I load a controller only when the template is requested?

2 Answers 2

5

I use RequireJS for this. And an in a provider, say lazyProvider:

In your lazy provider...

this.resolve = function(controller){
    return { myCtrl: ['$q',function ($q){
            var defer = $q.defer();
            require(['/app/controllers/'+controller,'myApp'],function(ctrl,myApp){
                myApp.register.controller(controller,ctrl);
                defer.resolve();

            }
            return defer.promise;
        }]
    };
};

In your ui-router resolve: property do:

resolve: lazyProvider.resolve('myCtrl');

You'll need to expose provider registers on your app so you can register them later, like:

myApp.config(function($urlRouterProvider, $stateProvider,
          $controllerProvider, $compileProvider, $filterProvider,$provide){
    //If your myApp isn't already a module...
    define('myApp',[],function(){return myApp});
    myApp.register = {
        controller: $controllerProvider.register,
        directive: $compileProvider.directive,
        filter: $filterProvider.register,
        factory: $provide.factory,
        service: $provide.service,
        constant: $provide.constant
    }

And your controllers:

define(['/dependencies'],function(deps){
    function MyCtrl($scope){...}
    return MyCtrl;
}

This is essentially what Dan Wahlin has shared here

Sign up to request clarification or add additional context in comments.

3 Comments

Any chance you have a GitHub sample? Been at this a while and hitting all kinds of issues.
Hey, Sorry, I haven't had time. Essentially I implement it similarly to the the blog post linked in the answer. What issues have you been having in particular?
@brayan ,@mbursill . I have implemented your way and would like to know how $stateChangeStart would work??? where to put it? how stateChangeStart gets triggered when state is changed? please help me as soon as possible.... waiting for your reply.. Can you show me demo at plunk link said above by calebboyd?
1

After playing around with @calebboyd sample, I got it working using System.js and ES6. What I liked about this implementation is that using ES6 modules, everything depends only on the file names which are already unique, so you don't need to care about controller names conflicting or even naming them. Controllers are anonymous.

This implementation assumes you have a folder "pages" with template/controller pairs like this:

/pages
  /page1.html
  /page1.js
  /page2.html
  /page2.js

Once you access /#pages/page1, it will load both the template and the controller dynamically.

Here is your "app.js":

import angular from 'angular';
import 'angular-ui-router';

angular.module('app', ['ui.router'])
  .config(($stateProvider, $urlRouterProvider, $controllerProvider) => {

    // save reference to the register method, so we can use inside the 'resolve'
    var registerController = $controllerProvider.register;

    // register a single state that takes the page name as a parameter
    $stateProvider
      .state('pages', {
        url: "/pages/:name",

        // the url and the controller name are dynamically created
        templateUrl: $stateParams => "pages/" + $stateParams.name + ".html",
        controllerProvider: $stateParams => $stateParams.name + '_DynamicController as vm',

        resolve: {

          'ctrl': ($stateParams, $q) => {

            var script = 'pages/' + $stateParams.name;
            var controllerName = $stateParams.name + '_DynamicController';

            // once System.js loads the module, we register using the
            // saved registerController function with the dynamic name
            return System.import(script)
              .then(ctrl => registerController(controllerName, ctrl['default']));
          }
        }
      });
  });

Here is a sample controller in "page1.js":

export default class {
  constructor() {
    this.data = "inside the controller";
  }
}

Sample template in "page1.html":

<h1>Page1 Template</h1>
Loaded: {{ vm.data }}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.