Magento – Magento 2 & Angular & RequireJS: Firing angular.bootstrap with injected controllers etc

angularjsrequirejs

I am looking at implementing angular into the Magento 2 layout structure using requirejs and for individual page dependencies to be injected where required (which requirejs handles very well). The issue is that these dependencies will need to be included before the angular.bootstrap call, and I need some way of loading this after the dependencies have loaded without knowing which.

What I think I need is some kind of callback or event that triggers once all requirejs modules have been loaded, but I can't see that this exists in the documentation?

Example: Product Page

Load js/dependency/angular.js

Load js/app.js:

define(['angular'], function(angular)
{
    var testApp = angular.module('testApp', []);
    return testApp;
});

Load js/controller/product.js

define(['testApp','angular'], function(testApp, angular)
{
    testApp.controller('ProductController', ['$scope', function($scope)
    {
        $scope.product = [...]
    }]);
});

All the above makes sense, and the ProductController could have a dependency added for an angular directive which handles the tabs, or the product image gallery or anything at all, which is all taken care of by requirejs.

The issue is that the above will change depending on the page loaded, so how would I get requirejs to know that all the required resources for this particular page have loaded and only then bootstrap the app?

Load js/app/bootstrap.js

define(['angular'], function(angular)
{
    angular.element(function()
    {
        angular.bootstrap(document, ['testApp']);
    });
});

Best Answer

I have not completely solved this - and would still like suggestions, but I wanted to post my current 'solution':

Basically I load the js/app/bootstrap.js with a dependency shim which I can add to with other modules.

var config = {
    paths: {
        angular: 'Test_App/js/dependencies/angular.js',
        testApp: 'Test_App/js/app.js',
        angularBootstrap: 'Test_App/js/app-bootstrap.js'
    },
    shim: {
        angularBootstrap: {
            deps: [
                'testApp'
            ]
        }
    }
}

Then in my Test_Product module (or wherever) I can include another requirejs-config.js file which adds that modules controller etc to the dependencies of the angular bootstrap script.

var config = {
    paths: {
        testAppProductController: 'Test_Product/js/controllers/product'
    },
    shim: {
        angularBootstrap: {
            deps: ['testAppProductController']
        }
    }
}

Then just sequence any module which needs to load angular elements

<module name="Test_Product" setup_version="0.0.1">
    <sequence>
        <module name="Test_App" />
    </sequence>
</module>

Thus, because of the dependency of testApp within the controller combined with the dependency of the controller for the bootstrap, we successfully load everything in the correct order and I can inject my controller as needed like so:

<div ng-controller="ProductController">
    <?php echo $block->getChildHtml(); ?>
</div>

<script type="text/javascript">
    require([
        'testAppProductController'
    ])
</script>

However the js/controllers/product.js script actually loads on every page, as the requirejs-config files are merged and the script is loaded because of the dependency set against the bootstrap module, without this the controller module could load after the bootstrap which was my initial problem. So it technically has solved my problem, but I feel like this solution is negating some of the benefits of requirejs.

Related Topic