angularjsmeteorangular-meteormeteor-packages

Angular-Meteor - How can I include ng template in package-based design?


I have an Angular-Meteor application working. I would like to package Angular templates and associated controller into a Meteor package, and inject these templates into my main application by adding that package.

What is best approach?

Update 2015-08-26 - I figured out how to add a template, documented below. But how to have a Meteor package inject the template's Angular controller into the base application?

A key tie-in is Angular UI-router.

I have a base application that includes my package named packageprefix:packagename. Inside this package I have my code in the root of the package folder: myPackagedPage.ng.html - the Angular HTML template myPackagedPage.js - the associated Angular controller

From my main application, I tried creating a route to my Angular template like so:

angular.module('parentModule',[
    'angular-meteor',
    'ui.router',
    'angularify.semantic.sidebar'
])

.config(['$urlRouterProvider', '$stateProvider', '$locationProvider',
function($urlRouterProvider, $stateProvider, $locationProvider){
    console.log("app.js config!");
    $locationProvider.html5Mode(true);

    $stateProvider
        .state('home', {
            url: '/',
            templateUrl: 'client/views/home/home.ng.html',
            controller: 'HomeCtrl'
        })

        .state('myPackagedPage', {
            url: '/myPackagedPage',
            templateUrl: 'packageprefix_packagename/myPackagedPage.ng.html',
            controller: 'MyPackagedPageCtrl'
        })
    ;

    $urlRouterProvider.otherwise('/');

}])

The application successfully finds the myPackagedPage.ng.html file and renders it. But how to add the controller?

I tried adding this in my package but the controller functions does not get called.

console.log("myPackagedPage.js loaded");
angular.module('parentModule')

.controller('MyPackagedPageCtrl', ['$scope',
    function($scope){
        console.log("MyPackagedPageCtrl");
    }])
;

I get an error:

Argument 'MyPackagedPageCtrl' is not a function, got undefined

Solution

  • I have this working now. Here is the approach that works for me, to inject an Angular Controller + template in a Meteor package, into the containing application.

    In my package.js, I have this

    Package.onUse(function(api) {
        api.versionsFrom('1.1.0.3');
        api.use('angular:angular@1.4.4', 'client');
        api.use("urigo:angular@0.9.3", 'client');
        api.use("session@1.1.0", 'client');
    
        //api.use('angularui:angular-ui-router@0.2.15', 'client');
        api.addFiles('interests.js', 'client');
        api.addFiles('interests.ng.html', 'client');
    
        api.export("InterestsCtrl", "client")
    });
    

    Note you must export your controller, so that the parent application may access it.

    In my package, called ramshackle:bigd-interests, I have these files at the root level: package.js, interests.ng.html, and interests.js. interests.js injects the Angular controller, the Anguilar UI-router route to the template, and a sidebar link into the parent application. It accomplishes this by using the Meteor Session. I played with other means of doing this but Session was the only thing that worked. Just be careful to properly scope your session variable names.

    //add controllers
    var controllers = Session.get("BIGD.controllers");
    if (!controllers) controllers = {};
    
    var interestsCtrlSpec = "'$scope', InterestsCtrl";
    
    InterestsCtrl = function($scope){
        console.log("InterestsCtrl running");
    };
    controllers.InterestsCtrl = interestsCtrlSpec;
    Session.set("BIGD.controllers", controllers);
    
    //add routes
    var routes = Session.get("BIGD.routes");
    if (!routes) routes = {};
    routes.interests = {
        url: '/interests',
        templateUrl: 'ramshackle_bigd-interests_interests.ng.html',
        controller: 'InterestsCtrl'
    };
    Session.set("BIGD.routes", routes);
    
    //add sidebar links
    //the key determines sorting order
    var sidebar = Session.get("BIGD.sidebar");
    if (!sidebar) sidebar = {};
    sidebar["interests"] = {
        url: '/interests',
        templateUrl: 'ramshackle_bigd-interests_interests.ng.html',
        controller: 'InterestsCtrl',
        rank: 5
    };
    Session.set("BIGD.sidebar", sidebar);
    
    var interestsItem = {label: 'Interests', link: '/interests', icon: "rocket"};
    

    In my parent application's app.js , I dynamically loaded the controllers and routes from the session like this:

    angular.module('bigdam',[
        'angular-meteor',
        'ui.router',
        'angularify.semantic.sidebar',
        'nvd3',
        'leaflet-directive',
        'ui.router.history'
    ])
    
        .config(['$urlRouterProvider', '$stateProvider', '$locationProvider',
        function($urlRouterProvider, $stateProvider, $locationProvider){
            //console.log("app.js config!");
            $locationProvider.html5Mode(true);
    
            //add a static state
            $stateProvider
                .state('home', {
                    url: '/',
                    templateUrl: 'client/views/home/home.ng.html',
                    controller: 'HomeCtrl'
                });
    
            //add the dynamic routes/states from other Meteor packages
            for (var stateId in routes) {
                var route = routes[stateId];
                $stateProvider
                    .state(stateId, route);
            }
    
            $urlRouterProvider.otherwise('/');
        }])
    ;
    
    //Declare the controllers from plugins
    for (var controllerId in controllers) {
        var controllerSpec = controllers[controllerId];
        var controllerSpecArray = eval("[" + controllerSpec + "]")
        angular.module('bigdam').controller(controllerId, controllerSpecArray);
    }
    

    So now, when I create a new Meteor package, and follow the convention described above, its controllers, routes, and sidebar links get loaded into the main application.