Скрипт динамически загружать контроллеры для AngularJS


Хотелось бы услышать ваши мысли, идеи или пожелания на следующий код.

Я добавил в код на GitHub.

https://github.com/redbullzuiper/angularjs-dynamic-controllers


Обычно при подключении контроллера к DOM-элемент, с помощью ng-controller="myController"необходимо включить контроллер где-то на Вашей странице, например:

<script src="src/application/controllers/my-controller.js"></script>

Однако, при включении скрипта я писал в headон будет проходить через дом и искать все ng-controller атрибутами.

Он будет затем автоматически добавлять правильные контроллера в head страницы. После это будет сделано, включая все контроллеры, он будет вручную запустить приложение с помощью:

angular.bootstrap(document, ['module_name']);

Таким образом, все контроллеры получают загружены динамически, без необходимости включения всех контроллеров в head. Но вместо этого они только загружаются, когда звонил.

Не могу создать демо через скрипку, но рабочую демо можно найти здесь

Код:

window.onload = function() 
{
    var promises = [];
    var ngApp = document.querySelectorAll('[ng-app]')[0];
    var ngControllers = document.querySelectorAll('[ng-controller]');

    // Throw warning if angular is not found
    if(typeof angular == 'undefined')
    {
        console.warn("simplify error: Angular not found, operation canceled.");
        return;
    }

    // Throw warning if ng-app directive exists. Script will bootstrap the application manually
    if(ngApp)
    {
        console.warn("Please remove the ng-app directive. `Simplify` will bootstrap the application manually.");
        console.warn("This will also most likely fix the 'Uncaught Error: [$injector:modulerr]' error.");
    }

    // Append the scripts
    for(var i = 0;i < ngControllers.length;i++)
    {
        var promise = new Promise(function(resolve, reject) {
            var src = 'src/application/controllers/'+ ngControllers[i].getAttribute('ng-controller') + '.controller.js';
            var script = document.createElement('script');
                script.setAttribute('src', src);

            document.head.appendChild(script);

            script.addEventListener('load', function() {
                resolve(script);
            });
        });

        // Push promises to array to resolve them all together later on
        promises.push(promise);
    }

    // Resolve all promises then bootstrap the app
    // Without the use of promises, the bootstrap will start before all scripts are included
    // This results into an erro
    Promise.all(promises).then(function() {     
        angular.bootstrap(document, ['app']);
    });
}


Комментарии
1 ответ

Отзывы

Это интересный способ контроллеры нагрузки. Мне нравится приложение обещаний. Он чувствовал себя немного странно не иметь ng-app директива, но это не имеет смысла, учитывая характер вашего кода.

Предложения

Приведенные ниже рекомендации могут упростить код, но не обязательно будет оптимальной или желаемой поддержки браузера.

Итерации функционально

Можно использовать функциональный подход, а не декларативной for петли:


for(var i = 0;i < ngControllers.length;i++)

Поскольку каждая итерация толкает обещание в массив promises, что цикл может быть заменен на вызов Array.map():

var promises = Array.prototype.map.call(ngControllers, function(ngController) 

Или .map можно назвать на массив после преобразования узла в массив через массив.ОТ() (но остерегайтесь браузер совместимости на что)

var promises = Array.from(ngControllers).map(function(ngController)

Или совершенно Эс-6 подхода, использовать спред оператора:

var promises = [...ngControllers].map(function(ngController)

В конце каждой итерации, просто вернуть обещают, а не толкая его в массив.

    // Push promises to array to resolve them all together later on
return promise;//promises.push(promise);
}); //end the call to .map()

Имейте в виду, что функциональный подход, вероятно, будет несколько медленнее, потому что вызов функции для каждой итерации, но для небольшого количества контроллеров это должно быть незначительное различие. Если вы хотите более подробно ознакомиться с функциональными JavaScript, я рекомендую эти упражнения.

Использование частичной(-лы прикладные) функции для обратных вызовов

Вместо закрытия просто позвонить resolve():


script.addEventListener('load', function() {
resolve(script);
});

Использовать Function.bind() (другое, чем (angular.bind()) для создания частично примененную функцию для каждого время resolve() называется.

script.addEventListener('load', resolve.bind(null, script));

Имейте в виду, что событие слушатель будет сдать event аргумент обратного вызова, но это должен быть второй аргумент, когда script связан в качестве первого аргумента.

То же самое верно для вызова функции загрузки после все обещания были решены:


Promise.all(promises).then(function() {       
angular.bootstrap(document, ['app']);
});

Может быть упрощена, как показано ниже:

Promise.all(promises).then(angular.bootstrap.bind(null, document, ['app']));

Использовать addEventListener вместо onload

Это, скорее всего, не будет проблемой в маленьком приложении, но в случае, если там был другой код (возможно, нескольких функций), которые должны выполняться при загрузке страницы, это может быть проще просто использовать window.addEventListener('DOMContentLoaded', function() {...}) вместо window.onload = function() {...};

2
ответ дан 29 марта 2018 в 04:03 Источник Поделиться