start-angular / sb-admin-angular

[DEPRECATED] Starter template / theme for AngularJS Dashboard Apps
http://startangular.com/
Other
841 stars 432 forks source link

Lazy loading is not being used correctly #41

Open yjukaku opened 8 years ago

yjukaku commented 8 years ago

The resolve method of angular-ui router is a map of functions. Each function should return a promise. When every function in the map has resolved, the state can continue loading.

However, in sb-admin, there are multiple calls like this in app.js:

resolve: {
            loadMyDirectives : function($ocLazyLoad){
                return $ocLazyLoad.load(
                {
                    name:'myApp',
                    files:[
                    'bower_components/moment/moment.js',
                    ]
                })
                ,
                $ocLazyLoad.load(
                {
                  name:'angulartics-google-analytics',
                  files:['bower_components/angulartics-google-analytics/dist/angulartics-google-analytics.min.js']
                })  //since there is a comma missing here, the return statement is triggered and the rest is ignored
                $ocLazyLoad.load(
                {
                   name:'toggle-switch',
                   files:["bower_components/angular-toggle-switch/angular-toggle-switch.min.js",
                          "bower_components/angular-toggle-switch/angular-toggle-switch.css"
                      ]
                })
        }

Calling $ocLazyLoad.load will load the items, but doing it repeatedly doesn't cause the state to wait for all the load calls to resolve, it only waits for the last one.

FURTHER, there are commas missing between some of the load calls after a return statement. This causes javascript to do ASI (automatic semicolon insertion), effectively ignoring any $ocLazyLoad.load items after the first missing comma.

*Note: This is my understanding, I may be wrong about how lazy load works. But the ASI is definitely happening, since you can write whatever valid JS you want after the missing comma and it never gets executed.

garciaba commented 8 years ago

But if you write a comma between the different lazyloaders it should work, right?

I don't really understand why there is no comma between loaders in the dashboard state, but there is (or should be, at least I must put it to add more libraries) in the other states.

Anyway, I'm getting an issue that is probably related to this. When switching between views, sometimes it throws an error saying that the controller does not exist (probably not loaded yet). If yor refresh the page it works perfectly, but I don't know what is happening there. I guess that the loader of that specific state is not yet loaded, but I'm not totally sure...

¿Any way to keep the view waiting until all directives/controllers are loaded?

yjukaku commented 8 years ago

It's not just the commas that are needed, but rather the whole thing needs to be wrapped in a Promise.all() and returned. On Mar 16, 2016 5:39 AM, "mikeyxoel" notifications@github.com wrote:

But if you write a comma between the different lazyloaders it should work, right?

I don't really understand why there is no comma between loaders in the dashboard state, but there is (or should be, at least I must put it to add more libraries) in the other states.

Anyway, I'm getting an issue that is probably related to this. When switching between views, sometimes it throws an error saying that the controller does not exist (probably not loaded yet). If yor refresh the page it works perfectly, but I don't know what is happening there. I guess that the loader of that specific state is not yet loaded, but I'm not totally sure...

¿Any way to keep the view waiting until all directives/controllers are loaded?

— You are receiving this because you authored the thread. Reply to this email directly or view it on GitHub https://github.com/start-angular/sb-admin-angular/issues/41#issuecomment-197228827

garciaba commented 8 years ago

Is there anyway to fix this issue? I'm quite new to AngularJS

yjukaku commented 8 years ago

Yep. Every $ocLazyLoad.load() returns an object (Promise object). Take every Promise object returned, and put it into an array. Then call $q.all(promisesArray), and return the result of that call.

This is necessary because AngularUI Router (which is what SBAdminAngular uses for routing URLs to states) expects a Promise object to return from the resolve function. If you have multiple promises that need to be finished from within resolve function, you can wrap it in $q.all. ($q is the Promise library for Angular)

garciaba commented 8 years ago

Could you make a Plunkr, or just a small example right here? I'm trying to do it but I just can't get it work. Would be great, thank you!

yjukaku commented 8 years ago
resolve: {
            loadMyDirectives : function($ocLazyLoad){
                var prom1 = $ocLazyLoad.load(
                {
                    name:'myApp',
                    files:[
                    'bower_components/moment/moment.js',
                    ]
                });
                var prom2 = $ocLazyLoad.load(
                {
                  name:'angulartics-google-analytics',
                  files:['bower_components/angulartics-google-analytics/dist/angulartics-google-analytics.min.js']
                });
                var prom3 = $ocLazyLoad.load(
                {
                   name:'toggle-switch',
                   files:["bower_components/angular-toggle-switch/angular-toggle-switch.min.js",
                          "bower_components/angular-toggle-switch/angular-toggle-switch.css"
                      ]
                });
                //prom1 "resolves" when it's files have all loaded
                //prom2 "resolves" when it's files have all loaded
                //etc.
                var promArray = [prom1, prom2, prom3]; //an array of Promise objects
                //we must return a single promise, but we have 3.  How do we solve this? It's built into the $q library from Angular.
                return $q.all(promArray); //create and return a wrapping Promise that resolves only when all the promises in the array have resolved.
        }
garciaba commented 8 years ago

I'm doing as you said, but for some reason the page just doesn't load, it stays white... There is not even error in the console. If I set a debugger in the $stateProvider it triggers, but I don't know...

// Now we set up the states
        $stateProvider
            .state('dashboard', { //parent view
                url: '/dashboard',
                templateUrl: 'views/dashboard/main.html',
                resolve: { // Any property in resolve should return a promise and is executed before the view is loaded
                    loadMyDirectives: function ($ocLazyLoad) {
                        var prom1 = $ocLazyLoad.load({
                            name: 'sbAdminApp',
                            files: [
                    'scripts/directives/header/header.js',
                    'scripts/directives/header/header-notification/header-notification.js',
                    'scripts/directives/sidebar/sidebar.js',
                    'scripts/directives/sidebar/sidebar-search/sidebar-search.js'
                    ]
                        });
                        var prom2 = $ocLazyLoad.load({
                            name: 'toggle-switch',
                            files: ["bower_components/angular-toggle-switch/angular-toggle-switch.min.js",
                          "bower_components/angular-toggle-switch/angular-toggle-switch.css"
                      ]
                        });
                        var prom3 = $ocLazyLoad.load({
                            name: 'ngAnimate',
                            files: ['bower_components/angular-animate/angular-animate.js']
                        });
                        var prom4 = $ocLazyLoad.load({
                            name: 'ngCookies',
                            files: ['bower_components/angular-cookies/angular-cookies.js']
                        });
                        var prom5 = $ocLazyLoad.load({
                            name: 'ngResource',
                            files: ['bower_components/angular-resource/angular-resource.js']
                        });
                        var prom6 = $ocLazyLoad.load({
                            name: 'ngSanitize',
                            files: ['bower_components/angular-sanitize/angular-sanitize.js']
                        });
                        var prom7 = $ocLazyLoad.load({
                            name: 'ngTouch',
                            files: ['bower_components/angular-touch/angular-touch.js']
                        });
                        var prom8 = $ocLazyLoad.load({
                            name: 'googlechart',
                            files: ['bower_components/angular-google-chart/ng-google-chart.js']
                        });

                        var promArray = [prom1, prom2, prom3, prom4, prom5, prom6, prom7, prom8];

                        return $q.all(promArray); 

                    }
                }
yjukaku commented 8 years ago

You have to inject $q into your loadMyDirectives function like loadMyDirectives: function ($ocLazyLoad, $q). If you need further help, post a SO question. :+1:

garciaba commented 8 years ago

Thank you, it totally works, and very good actually! You should make a pull request.