AngularJS directive loaded with RequireJS not compiling - angularjs

I'm in the beginning stages of building a large app with AngularJS and RequireJS. Everything loads find but directives aren't manipulating the DOM as they should. No errors are being reported and rest of the app works fine: Views are loaded and $scope is bindable. Examining the console shows that all the files loaded. I'm assuming this is a lazy load issue in that my directive is simply not loading at the correct time. I'd appreciate any insight into how to properly load directives in this regard. Unless it's a part of Angular's jqLite, please refrain from suggesting jQuery.
config.js
require.config({
paths: { angular: '../vendor/angular' }
shim: { angular: { exports: 'angular' } }
});
require(['angular'], function(angular) {
angular.bootstrap(document, ['myApp']);
});
myApp.js
define(['angular', 'angular-resource'], function (angular) {
return angular.module('myApp', ['ngResource']);
});
routing.js
define(['myApp', 'controllers/mainCtrl'], function (myApp) {
return myApp.config(['$routeProvider', function($routeProvider) {
...
}]);
});
mainCtrl.js
define(['myApp', 'directives/myDirective'], function (myApp) {
return myApp.controller('mainCtrl', ['$scope', function ($scope) {
...
}]);
});
myDirective.js
require(['myApp'], function (myApp) {
myApp.directive('superman', [function() {
return {
restrict: 'C',
template: '<div>Here I am to save the day</div>'
}
}])
});
home.html
<div class="superman">This should be replaced</div>
home.html is a partial that's loaded into ng-view

Angular cannot load directives after it has been bootstrapped. My suggestion is:
Make myDirective.js do a define(), not a require()
Make sure myDirective.js is run before the require(['angular'],...) statement in config.js, e.g. do require(['angular','myDirective'],...). For this to work, myDirective should be shimmed to depend on angular - thanks # David Grinberg.
As a sidenote, take a look at this in Stackoverflow/this in GitHub, we have been trying to do RequireJS + Angular play together.

Related

AngularJS - $templateCache is not defined

I am trying to load a template file in an AngularStrap popover, however I am having trouble using $templateCache. I seem to be a step further back than the other SO questions, hence this seemingly double one.
Following the API docs I added a <script type="text/ng-template" id="popoverTemplate.html"></script> right before the closing </body> tag. When I use <div ng-include="'popoverTemplate.html'"></div> on my page, I get nothing. If I try using console.log($templateCache.get("popoverTemplate.html")) I get "$templateCache is not defined", which leads me to assume I am missing a crucial step. However, I can't find how to do it in the docs or other SO questions.
EDIT:
Injecting the service was the missing link. However, when I inject the service, the controller's other function no longer works, but if you inject al the function's parameters the working code becomes:
(function() {
"use strict";
angular.module("app").controller("managerController", ["$scope", "imageHierarchyRepository", "$templateCache", function ($scope, imageHierarchyRepository, $templateCache) {
imageHierarchyRepository.query(function(data) {
$scope.hierarchies = data;
});
var template = $templateCache.get("popoverTemplate.html");
console.log(template);
}]);
})();
To use the template script tag . You have to insert it inside the angular application. That is inside the element with the ng-app attribute or the element used to bootstrap the app if you don't use the ng-app tag.
<body ng-app="myapp">
<div ng-template="'myTemplate.html'"></div>
<script type="text/ng-template" id="myTemplate.html">
// whate ever
</script>
</body>
If you want to retrieve the template on a component of the application then you need to inject the service where you want to consume it:
controller('FooCtrl', ['$templateCache', function ($templateCache) {
var template = $templateCache.get('myTemplate.html');
}]);
Or
controller('FooCtlr', FooCtrl);
FooCtrl ($templateCache) {};
FooCtrl.$inject = ['$templateCache'];
EDIT
Do not register two controllers with the same name because then you override the first one with the last one.
(function() {
"use strict";
angular.module("app").controller("managerController",["$scope", "imageHierarchyRepository", "$templateCache", function ($scope, imageHierarchyRepository, $templateCache) {
var template = $templateCache.get("popoverTemplate.html");
console.log(template);
imageHierarchyRepository.query(function(data) {
$scope.hierarchies = data;
});
}]);
})();
Small addition: Although there are few ways to achieve your goals, like wrapping your whole HTML in <script> tags and all that, the best approach for me was to add the $templateCache logic into each Angular directive. This way, I could avoid using any external packages like grunt angular-templates (which is excellent but overkill for my app).
angular.module('MyApp')
.directive('MyDirective', ['$templateCache', function($templateCache) {
return {
restrict: 'E',
template: $templateCache.get('MyTemplate').data,
controller: 'MyController',
controllerAs: 'MyController'
};
}]).run(function($templateCache, $http) {
$http.get('templates/MyTemplate.html').then(function(response) {
$templateCache.put('MyTemplate', response);
})
});
Hope this helps!

Accessing directive from a different module

I have two different modules , first one contains controller second directive.
Basically i want the directive to be rendered in my view.
But as modules for directiveandcontroller are different, its giving error.
If i give same module then it ressolves my issue by i want a generic module serving all controllers.
controller
angular.module('SlamModuleCtrl')
.controller('SlambookExt',['$scope','$route','SlambookCollection', function($scope,$route) {
console.log("slam controller")
}]);
Directive
angular.module('Genericmodule')
.directive('appVersion', ['version', function (version) {
return {
restrict: 'E',
template: '<h1>version1</h1>'
}
}])
View
<div ng-controller="SlambookExt">
<app-version>1</app-version>
</div>
When instantiating slamModuleCtrl, you need to specify genericModule as a dependency.
Or use a parent module that loads both those modules as dependencies and use that parent module as your ng-app.
angular.module('parentModule',['slamModuleCtrl','genericModue'])
This is just a plausible solution because both your modules look right to me. So I'm guessing that the version is not showing up because the module hasn't been loaded
Chanthu is correct you need to specify a parent module that has dependencies on your other modules but you also pass a array of dependencies for your other modules, in this case they don't have any.
declare your modules and add them in to the parent module like so...
var controllerModule = angular.module('controllerModule', []);
var directiveModule = angular.module('directiveModule', []);
controllerModule.controller('mainController', function($scope) {
$scope.hello = 'Hello';
})
directiveModule.directive('myDirective', function() {
return {
template: '{{hello}}'
};
});
angular.module('app', ['controllerModule', 'directiveModule']);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
<body ng-app="app" ng-controller="mainController">
<my-directive></my-directive>
</body>
code snippet shows another module directive and uses the binding from another module controller

AngularJS + RequireJS + angular-ui-grid

I am trying to use angular-ui-grid with AngularJS and RequireJS. See plunker here.
My index31.html has grid and indexController.js defines the gridOptions object. indexController is injected when needed.
When browser loads indexController.js before index31.html, it works fine (i.e. grid is displayed) but when it is the other way round, I get error: $scope.uiGrid is undefined.
How do I specify (in $stateProvider config or elsewhere) to always load indexController.js before index31.html. Or, how do I make all controllers load before the html?
The reason for this is that you require the actual code of the controller asynchronously, i.e. with an inline require:
// app.js
define(function () {
...
app.controller('IndexController', ['$scope', '$injector', function ($scope, $injector) {
// HERE!!!
require(['indexController'], function (controller) {
$injector.invoke(controller, this, { '$scope': $scope });
});
}]);
});
There is no guarantee for the order of loading with this pattern.
What can you do: Require the 'indexController' at the top:
define(['indexController'], function (indexController) {
...
app.controller('IndexController', indexController);
});
It even removes the (horrible IMO) usage of $injector!
(Sidenote: Doing this, the plunk complained about the $scope.$apply() in the last line of indexController.js; I commented it out, it really seems redundant.)
Plunk: http://plnkr.co/edit/fsyljR8FEeZdvXB3SRJP?p=preview

Require and Angular RouteProvider undefined

I get undefined in the following code when trying to load via require.js
HTML
<script data-main="application/main" src="http://requirejs.org/docs/release/2.1.14/minified/require.js"></script>
main.js
require.config({
baseUrl: "application",
paths: {
angular: 'https://code.angularjs.org/1.4.5/angular',
angularRoute: 'https://code.angularjs.org/1.4.5/angular-route'
},
shim: {
angular: {exports: 'angular' } ,
angularRoute: { deps: ['angular'], exports: 'angularRoute' },
}
});
require(['app', 'routesBoot'], function (app) {
app.init();
});
app.js
define(['angular'], function (angular) {
var app = angular.module('reporterdashboard', []);
app.init = function () {
console.log('app.init called');
angular.bootstrap(document, ['reporterdashboard']);
};
return app;
});
routesBoot.js
require(['app', 'angularRoute'], function (app, angularRouterParam) {
//put my routes in here, using angularRouterParam
//but angularRouterParam = undefined
return app.config(function (angularRouterParam) {
angularRouterParam.when('/page2', { controller: 'Page2Controller', templateUrl: 'page2.html' });
});
});
When I inspect angularRouterParam passed into routesBoot it's undefined. What have I done wrong ?
I'm basically trying to split my app and therefore, placing my routes in their own file (controllers, directives etc will live in their own boot .js files). I'm letting require.js look after all my .js file loading as can be seen in main.js.
The code in routesBoot is not syntactically correct at the moment as I'm stuck with the problem of an undefined angularRouterParam
The problem was out side of the code I've posted here. I was loading a directive first into the index.html (View First). This directive was loading other dependencies. The project is quite large, some 40 .js files. My restructuring with require was at fault.

AngularJS and RequireJS: No module: myApp

I'm trying for the first time to use AngularJS in conjunction with RequireJS using this guide as a basis. As far I can tell after a lot of debugging I'm loading all my modules in the correct order, but when the application runs Angular throws an Error / Exception with the following message:
Argument 'fn' is not a function, got string from myApp
I've seen this message before due to syntax errors, so even though I've looked trough the code multiple times I won't rule out the possibility of a simple syntax error. Not making a Fiddle just yet in case it is something as simple as a syntax error, but I'll of course do so if requested.
Update: I just noticed when setting ng-app="myApp" in the <html> tag I also get an additional error,
No module: myApp
Update II: Okay, it turns out it indeed was an syntax error in the only file not included below. I am though still left with the problem from update I.
RequireJS bootstrap
'use strict';
define([
'require',
'angular',
'app/myApp/app',
'app/myApp/routes'
], function(require, ng) {
require(['domReady'], function(domReady) {
ng.bootstrap(domReady, ['myApp']);
});
});
app.js
'use strict';
define([
'angular',
'./controllers/index'
], function(ng) {
return ng.module('myApp', [
'myApp.controllers'
]);
}
);
controllers/index
'use strict';
define([
'./front-page-ctrl'
], function() {
});
controllers/module
'use strict';
define(['angular'], function (ng) {
return ng.module('myApp.controllers', []);
});
controllers/front-page-ctrl
'use strict';
define(['./module'], function(controllers) {
controllers.
controller('FrontPageCtrl', ['$scope',
function($scope) {
console.log('I\'m alive!');
}
]);
});
Delete ng-app="myApp" from your html.
Because it has bootstrapped manually
ng.bootstrap(domReady, ['myApp']);
RequireJS docs on Dom ready state:
Since DOM ready is a common application need, ideally the nested
functions in the API above could be avoided. The domReady module also
implements the Loader Plugin API, so you can use the loader plugin
syntax (notice the ! in the domReady dependency) to force the
require() callback function to wait for the DOM to be ready before
executing. domReady will return the current document when used as a
loader plugin:
So, when you require 'domReady' the result is a function:
function domReady(callback) {
if (isPageLoaded) {
callback(doc);
} else {
readyCalls.push(callback);
}
return domReady;
}
But when you append the domReady string with ! sign the result will be the actual document element:
'use strict';
define([
'require',
'angular',
'app/myApp/app',
'app/myApp/routes'
], function(require, ng) {
require(['domReady!'], function(domReady) {
// domReady is now a document element
ng.bootstrap(domReady, ['myApp']);
});
});

Resources