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
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.