How can I pass argument with parameter to gulp? - angularjs

I want to do something like
gulp build --param1=abc
and I want this param to be available in Angular Service/Controller.
Is there a way I can do that?

Yes. I would recommend in your Gulp config, you create an Angular constant that can be injected into any part of the application.
var ngConstant = require('gulp-ng-constant');
var args = require('yargs').argv;
gulp.task('constant', function () {
var selectedConstant = args.env || 'dev'; // if --env is not declared, assume dev
var constants = {
dev: {
ENV: {
'api': 'api.someUrl.com',
'url': 'dev.url.com'
}
},
prod: {
ENV: {
'api': 'api.someUrl.com',
'url': 'dev.url.com'
}
},
};
return ngConstant({
name: 'app', // name of your module
constants: constants[selectedConstant],
stream: true
})
.pipe(gulp.dest('src'));
});
gulp.task('build', function () {
runSequence(
['constant']
)
});
Now, you can run gulp build --env=prod and that will create a file called constants.js in your /src/ folder (or wherever you specify). Now include this file in your index.html.
You can now inject the constant ENV anywhere in your services or controllers and reference the variables within.
angular.module('app')
.service('SomeService', function(ENV) {
// access ENV vars here
});
.controller('SomeCtrl', function($scope, ENV) {
// access ENV vars here
});

Related

Grunt-browserify external libs give: Cannot find module

I'm working on app with Backbone Marionette and i'm building my files with grunt-browserify 3.8.
Backbone, Marionette, underscore and jQuery are added with npm.
I'm compiling all my files in one single file.
Everything was working fine but the build was extremly slow (like 1.5mins) so i read about using external config option in grunt-browserify.
Now the build is quite fast but when i access to the page i got:
Uncaught Error: Cannot find module 'underscore' to the line when i first use my require function
I read everywhere but i cannot figured out the correct configuration for grunt-brwoserify.
Here is my GruntFile:
'use strict';
module.exports = function (grunt) {
require('grunt-config-dir')(grunt, {
configDir: require('path').resolve('tasks')
});
require('jit-grunt')(grunt);
// show elapsed time at the end
require('time-grunt')(grunt);
grunt.registerTask('i18n', [ 'clean', 'dustjs', 'clean:tmp' ] ); // not used for now
grunt.registerTask('build', ['i18n', 'less', 'cssmin', 'browserify', 'concurrent:build'] );
grunt.registerTask('serve', [ 'build', 'concurrent:server'] ); // Build & run the server locally, for development.
};
Here my Browserify task:
'use strict';
module.exports = function browserify(grunt) {
// Load task
grunt.loadNpmTasks('grunt-browserify');
// Options
return {
build: {
files: {
'.build/js/theme-smarty.js': ['public/js/assets/smarty-themeApp/plugin/jquery.min.js', 'public/js/assets/smarty-themeApp/**/*.js'],
'.build/js/app-bundled.js': ['public/js/app.js'],
'.build/js/landing-page.js': ['public/js/landing-page.js']
// '.build/js/app-admin-bundled.js': ['public/js/app-admin.js']
},
options: {
// activate watchify
watch: true,
watchifyOptions: {
spawn: false
},
include: [
'public/js/**/*.js'
],
transform: [['babelify', {'presets': 'es2015', 'compact': false}]],
external: [
'backbone',
'underscore',
'jquery',
'backbone.marionette'
]
}
}
};
};
And here my first views where i require libs:
'use strict';
const
_ = require('underscore'),
$ = require('jquery'),
Backbone = require('backbone'),
Marionette = require('backbone.marionette'),
MainRouter = require('./main-router'),
MainController = require('./main-controller');
Backbone.$ = $;
let View = Marionette.LayoutView.extend({
template: require('./main-page.dust'),
regions: {
mainContainer: '.main-container',
modalContainer: '.modal-container'
},
initialize: function () {
this.model = new Backbone.Model({
page: this.$el.data('page')
});
new MainRouter({
controller: new MainController({
mainContainer: this.mainContainer,
modalContainer: this.modalContainer
})
});
},
onRender: function () {
Backbone.history.start({pushState: true});
}
});
module.exports = View;
Look like libs are not even compiled in my app-bundled.js files.
What's the best/correct way to compile them?
Is better to have two separate files? libs and app?
Is possible to do with just one file using libs from npm?

No Directive annotation found on TodoApp Server Rendering

I'm trying to do server rendering with angular2-universal. I copy paste the example todo app of the official repo https://github.com/angular/universal/tree/master/examples/src/universal/todo into my own Trails/Express server.
I manage to start my server but when I call http://localhost:3000 I have the following error :
Error: No Directive annotation found on TodoApp
at new BaseException (/Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/#angular/compiler/src/facade/exceptions.js:17:23)
at DirectiveResolver.resolve (/Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/#angular/compiler/src/directive_resolver.js:31:15)
at CompileMetadataResolver.getDirectiveMetadata (/Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/#angular/compiler/src/metadata_resolver.js:55:51)
at RuntimeCompiler.resolveComponent (/Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/#angular/compiler/src/runtime_compiler.js:34:47)
at /Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/#angular/core/src/application_ref.js:99:37
at /Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/#angular/core/src/application_ref.js:292:26
at ZoneDelegate.invoke (/Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/zone.js/dist/zone-node.js:281:29)
at Object.NgZoneImpl.inner.inner.fork.onInvoke (/Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/#angular/core/src/zone/ng_zone_impl.js:45:41)
at ZoneDelegate.invoke (/Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/zone.js/dist/zone-node.js:280:35)
at Zone.run (/Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/zone.js/dist/zone-node.js:174:44)
at NgZoneImpl.runInner (/Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/#angular/core/src/zone/ng_zone_impl.js:76:71)
at NgZone.run (/Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/#angular/core/src/zone/ng_zone.js:223:66)
at ApplicationRef_.run (/Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/#angular/core/src/application_ref.js:290:14)
at Object.coreLoadAndBootstrap (/Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/#angular/core/src/application_ref.js:96:19)
at /Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/angular2-universal/dist/node/bootloader.js:186:34
at Array.map (native)
The example of the universal repo is working so I don't understand why it's not working for me. I don't change anything on the angular2 sources.
All my code is here https://github.com/jaumard/trails-angular2-isomorphic with the configuration here https://github.com/jaumard/trails-angular2-isomorphic/blob/master/api/controllers/ViewController.js#L58 for the route and here for the template engine https://github.com/jaumard/trails-angular2-isomorphic/blob/master/config/web.js#L76
Your problem is that you're working with different packages angular2 at the same time.
You start server with modules from ./node_modules/ folder but your component TodoApp will be decorated by instance ComponentMetadata (extends DirectiveMetadata) from ./dist/src/node_modules/#angular/core/ folder.
I think that first you need to transfer packages to dist folder and then run server. And you have to use the same path to angular2 modules (./dist/src/node_modules)
For example you can try something like this:
server.js
'use strict'
const gulp = require('gulp');
const rimraf = require('gulp-rimraf');
const path = require('path');
const dest = './dist';
gulp.task('clean', (cb) => {
return gulp.src(dest).pipe(rimraf())
});
gulp.task('prerun', ['clean'], () => {
return gulp.src([
'node_modules/rxjs/**/*',
'node_modules/zone.js/**/*',
'node_modules/core-js/**/*',
'node_modules/reflect-metadata/**/*',
'node_modules/systemjs/**/*',
'node_modules/#angular/**/*',
'node_modules/angular2-universal/**/*',
'node_modules/angular2-universal-polyfills/**/*',
'node_modules/angular2-express-engine/**/*',
'node_modules/angular2-hapi-engine/**/*'
], { base: './' })
.pipe(gulp.dest(path.join(dest, 'src')))
});
gulp.start('prerun', run)
function run() {
require('./dist//src/node_modules/angular2-universal/polyfills')
const app = require('./')
const TrailsApp = require('trails')
const server = new TrailsApp(app)
server.start().catch(err => server.stop(err))
}
I added reflect-metadata in package.json. Also you need to change a bit componentUrl like this:
ViewController.js
module.exports = class ViewController extends Controller {
helloWorld(req, res) {
const todoApp = require('../../dist/src/todo/app')
let queryParams = ng2U.queryParamsToBoolean(req.query);
let options = Object.assign(queryParams , {
// client url for systemjs
buildClientScripts: true,
systemjs: {
componentUrl: 'todo/browser', <== remove src/
map: {
'angular2-universal': 'node_modules/angular2-universal',
'#angular': 'node_modules/#angular'
},
packages: PACKAGES
},
...
Then you can see the following error:
So you need to add rxjs in your configuration:
ViewController.js
const PACKAGES = {
'angular2-universal/polyfills': {
format: 'cjs',
main: 'dist/polyfills',
defaultExtension: 'js'
},
...
rxjs: { <== this property
defaultExtension: 'js'
}
};
...
systemjs: {
componentUrl: 'todo/browser',
map: {
'angular2-universal': 'node_modules/angular2-universal',
'#angular': 'node_modules/#angular',
'rxjs': 'node_modules/rxjs' <== this line
},
packages: PACKAGES
},
See also the full list of changes here https://github.com/alexzuza/trails-angular2-isomorphic/commit/45f2e59529821757f6a6c03c5872e08fdce3f3e7
Hope it helps you!

Configure value using a service constant

In my latest project I'm making some API requests.
As far as I need to setup different endpoints (local development, remote production), I set the value of the used endpoint when defining the app. Everything works just fine.
var app = angular.module('app', ['lelylan.dashboards.device', 'ngMockE2E'])
app.value('client.config', { endpoint: 'http://localhost:8000' });
The only problem is that I want to set the endpoint http://localhost:8000 from a service that defines some constants [1]. I tried using the run and config block, but the value is not set. I was doing something like this, without any result.
var app = angular.module('app', ['lelylan.dashboards.device', 'ngMockE2E'])
.config(function(ENV) {
app.value('lelylan.client.config', { endpoint: ENV.endpoint });
})
This looks quite terrible to me, but I've no idea on how to solve this issue.
Thanks a lot.
[1] grunt-ng-constant
Please see here : http://jsbin.com/foqar/1/
var app = angular.module('app', []);
var client = {
endpoint : 'http://localhost:8000'
}
app.value('config', client);
app.controller('firstCtrl', function($scope, config){
$scope.endpoint = config.endpoint;
});
or: http://jsbin.com/foqar/2/edit
var app = angular.module('app', []);
app.value('config',{client :{endpoint : 'http://localhost:8000'}});
app.controller('firstCtrl', function($scope, config){
$scope.endpoint = config.client.endpoint;
});
It looks like the grunt-ng-constant creates a new module file to define the constants. You do not have to bother about it on your app JS file, other than declare the module containing those constants as a dependency.
The below is taken as is on the example config of the documentation of grunt-ng-constant
grunt.initConfig({
ngconstant: {
options: {
name: 'config',
dest: 'config.js',
constants: {
title: 'grunt-ng-constant',
debug: true
}
},
dev: {
constants: {
title: 'grunt-ng-constant-beta'
}
},
prod: {
constants: {
debug: false
}
},
}
});
In the options section, you specify the name of the module, file for the module to be written to and a general set of constants. It works like this,
options: {
name: 'config',
dest: 'config.js',
constants: {
title: 'grunt-ng-constant',
debug: true
}
}
The above code will become,
/*File: config.js*/
angular.module('config', [])
.constant('title', 'grunt-ng-constant')
.constant('debug', true);
To change the constants based on your scenario (development / production) you would be using different task sets. Here is where the dev and prod section comes into play
Considering you are using ng-boilerplate, in the gruntfile.js you have tasks build and compile. The build task is used during development, and the compile gets your app ready to be pushed to production.
In the build task you would add ngconstant:dev and in the compile task you would add ngconstant:prod.
grunt.registerTask('build', ['clean', 'html2js', 'otherTasksComeHere', 'ngconstant:dev']);
grunt.registerTask('compile', ['clean', 'html2js', 'otherTasksComeHere', 'ngconstant:prod']);
For your scenario the code would be as below:
/*gruntfile.js*/
grunt.initConfig({
ngconstant: {
options: {
name: 'lelylan.client.config',
dest: 'config.js',
values: {
endpoint : 'http://localhost:8000'
}
},
dev: {
debug: true
},
prod: {
endpoint: 'http://www.production-server.com/',
debug: false
}
},
});
grunt.registerTask('build', ["ngconstant:dev"]);
grunt.registerTask('compile', ["ngconstant:prod"]);
grunt.registerTask.('default', ["build", "compile"]);
/*app.js*/
var app = angular.module('app', ['lelylan.dashboards.device', 'leylan.client.config', 'ngMockE2E']);
app.controller("appCtrl", ["$scope", "$http", "endpoint",
function($scope, $http, endpoint) {
$scope.submit = function(formData) {
$http.post(endpoint+'/processform.php', formData);
}
}]);
Now it all depends on whether you run grunt build or grunt compile. The default task is run when you use the grunt command.
The solution was to use providers. As the docs states:
during application bootstrap, before Angular goes off creating all
services, it configures and instantiates all providers. We call this
the configuration phase of the application life-cycle. During this
phase services aren't accessible because they haven't been created
yet.
For this reason I created a provider to set the needed configurations.
<script>
angular.module('app', ['lelylan.dashboards.device'])
angular.module('app').config(function(lelylanClientConfigProvider, ENV) {
lelylanClientConfigProvider.configure({ endpoint: ENV.endpoint });
});
</script>
In this way all services will then be able to use the new endpoint. Using .value this was not possible.
Thanks everyone for your help.

How to load environment specific configuration with requirejs?

I am making a single page app and using backbone with requirejs. Now I want to include different configurations depending environment variable. I don't know how can I do this and what is best practices.
I have three different config file:
config.js : This keep common configurations.
config.dev.js : This keeps development environment specific configurations
config.production.js : This keeps production specific configurations
I am extending Config model(in config.js) from environment specific model and I requiring the Config model from other modules.
This working fine but downloading dev and production config files both. I want only load config.dev.js or config.production.js not both.
// config.js
define([
'backbone',
'modules/config/config.dev',
'modules/config/config.production'
], function(Backbone, DevConfig, ProdConfig) {
// Select environment model
EnvConfig = (ENV == 'dev') ? DevConfig : ProdConfig;
var ConfigModel = EnvConfig.extend({
// Extending attributes.
defaults: _.extend({
foo : 'bar'
}, EnvConfig.prototype.defaults)
});
return new ConfigModel();
});
In other modules I am using Config as below style:
define([
'backbone',
'modules/config/config'
], function(Backbone, Config) {
var ProfileModel = Backbone.Model.extend({
urlRoot: Config.get('apiRoot') + '/profiles'
...
});
return ProfileModel;
});
How to load one of development or production specific config file, not both?
I'm not sure where the ENV variable comes from, but assuming it's just a global variable you could do something like this:
index.html
<script type="text/javascript">
// adding it here so the example works on its own
window.ENV = 'production'; // or 'dev'
// this will be added to RequireJS configuration
var require = {
paths: {
// 'env-config' will be an alias for actual file name
'env-config': 'config.' + window.ENV
}
};
</script>
<script data-main="scripts/main" src="scripts/requirejs.js"></script>
config.dev.js:
define({
'env': 'development!'
});
config.production.js:
define({
'env': 'production!'
});
main.js:
require(['config', 'env-config'], function(conf, envConfig) {
console.log('The environment is: ', envConfig.env);
});

Unit Testing AngularJS directive with templateUrl

Using AngularJS.
Have a directive.
Directive defines templateUrl.
Directive needs unit testing.
Currently unit testing with Jasmine.
This recommends code like:
describe('module: my.module', function () {
beforeEach(module('my.module'));
describe('my-directive directive', function () {
var scope, $compile;
beforeEach(inject(function (_$rootScope_, _$compile_, $injector) {
scope = _$rootScope_;
$compile = _$compile_;
$httpBackend = $injector.get('$httpBackend');
$httpBackend.whenGET('path/to/template.html').passThrough();
}));
describe('test', function () {
var element;
beforeEach(function () {
element = $compile(
'<my-directive></my-directive>')(scope);
angular.element(document.body).append(element);
});
afterEach(function () {
element.remove();
});
it('test', function () {
expect(element.html()).toBe('asdf');
});
});
});
});
Running code in Jasmine.
Getting error:
TypeError: Object #<Object> has no method 'passThrough'
templateUrl needs loading as-is
Cannot use respond
May be related to ngMock use rather than ngMockE2E use.
You're correct that it's related to ngMock. The ngMock module is automatically loaded for every Angular test, and it initializes the mock $httpBackend to handle any use of the $http service, which includes template fetching. The template system tries to load the template through $http and it becomes an "unexpected request" to the mock.
What you need a way to pre-load the templates into the $templateCache so that they're already available when Angular asks for them, without using $http.
The Preferred Solution: Karma
If you're using Karma to run your tests (and you should be), you can configure it to load the templates for you with the ng-html2js preprocessor. Ng-html2js reads the HTML files you specify and converts them into an Angular module that pre-loads the $templateCache.
Step 1: Enable and configure the preprocessor in your karma.conf.js
// karma.conf.js
preprocessors: {
"path/to/templates/**/*.html": ["ng-html2js"]
},
ngHtml2JsPreprocessor: {
// If your build process changes the path to your templates,
// use stripPrefix and prependPrefix to adjust it.
stripPrefix: "source/path/to/templates/.*/",
prependPrefix: "web/path/to/templates/",
// the name of the Angular module to create
moduleName: "my.templates"
},
If you are using Yeoman to scaffold your app this config will work
plugins: [
'karma-phantomjs-launcher',
'karma-jasmine',
'karma-ng-html2js-preprocessor'
],
preprocessors: {
'app/views/*.html': ['ng-html2js']
},
ngHtml2JsPreprocessor: {
stripPrefix: 'app/',
moduleName: 'my.templates'
},
Step 2: Use the module in your tests
// my-test.js
beforeEach(module("my.templates")); // load new module containing templates
For a complete example, look at this canonical example from Angular test guru Vojta Jina. It includes an entire setup: karma config, templates, and tests.
A Non-Karma Solution
If you do not use Karma for whatever reason (I had an inflexible build process in legacy app) and are just testing in a browser, I have found that you can get around ngMock's takeover of $httpBackend by using a raw XHR to fetch the template for real and insert it into the $templateCache. This solution is much less flexible, but it gets the job done for now.
// my-test.js
// Make template available to unit tests without Karma
//
// Disclaimer: Not using Karma may result in bad karma.
beforeEach(inject(function($templateCache) {
var directiveTemplate = null;
var req = new XMLHttpRequest();
req.onload = function() {
directiveTemplate = this.responseText;
};
// Note that the relative path may be different from your unit test HTML file.
// Using `false` as the third parameter to open() makes the operation synchronous.
// Gentle reminder that boolean parameters are not the best API choice.
req.open("get", "../../partials/directiveTemplate.html", false);
req.send();
$templateCache.put("partials/directiveTemplate.html", directiveTemplate);
}));
Seriously, though. Use Karma. It takes a little work to set up, but it lets you run all your tests, in multiple browsers at once, from the command line. So you can have it as part of your continuous integration system, and/or you can make it a shortcut key from your editor. Much better than alt-tab-refresh-ad-infinitum.
What I ended up doing was getting the template cache and putting the view in there. I don't have control over not using ngMock, it turns out:
beforeEach(inject(function(_$rootScope_, _$compile_, $templateCache) {
$scope = _$rootScope_;
$compile = _$compile_;
$templateCache.put('path/to/template.html', '<div>Here goes the template</div>');
}));
This initial problem can be solved by adding this:
beforeEach(angular.mock.module('ngMockE2E'));
That's because it tries to find $httpBackend in ngMock module by default and it's not full.
The solution I reached needs jasmine-jquery.js and a proxy server.
I followed these steps:
In karma.conf:
add jasmine-jquery.js to your files
files = [
JASMINE,
JASMINE_ADAPTER,
...,
jasmine-jquery-1.3.1,
...
]
add a proxy server that will server your fixtures
proxies = {
'/' : 'http://localhost:3502/'
};
In your spec
describe('MySpec', function() {
var $scope, template;
jasmine.getFixtures().fixturesPath = 'public/partials/'; //custom path so you can serve the real template you use on the app
beforeEach(function() {
template = angular.element('');
module('project');
inject(function($injector, $controller, $rootScope, $compile, $templateCache) {
$templateCache.put('partials/resources-list.html', jasmine.getFixtures().getFixtureHtml_('resources-list.html')); //loadFixture function doesn't return a string
$scope = $rootScope.$new();
$compile(template)($scope);
$scope.$apply();
})
});
});
Run a server on your app's root directory
python -m SimpleHTTPServer 3502
Run karma.
It took my a while to figure this out, having to search many posts, I think the documentation about this should be clearer, as it is such an important issue.
My solution:
test/karma-utils.js:
function httpGetSync(filePath) {
var xhr = new XMLHttpRequest();
xhr.open("GET", "/base/app/" + filePath, false);
xhr.send();
return xhr.responseText;
}
function preloadTemplate(path) {
return inject(function ($templateCache) {
var response = httpGetSync(path);
$templateCache.put(path, response);
});
}
karma.config.js:
files: [
//(...)
'test/karma-utils.js',
'test/mock/**/*.js',
'test/spec/**/*.js'
],
the test:
'use strict';
describe('Directive: gowiliEvent', function () {
// load the directive's module
beforeEach(module('frontendSrcApp'));
var element,
scope;
beforeEach(preloadTemplate('views/directives/event.html'));
beforeEach(inject(function ($rootScope) {
scope = $rootScope.$new();
}));
it('should exist', inject(function ($compile) {
element = angular.element('<event></-event>');
element = $compile(element)(scope);
scope.$digest();
expect(element.html()).toContain('div');
}));
});
If you are using Grunt, you can use grunt-angular-templates. It loads your templates in the templateCache and it's tranparent to your specs configuration.
My sample config:
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
ngtemplates: {
myapp: {
options: {
base: 'public/partials',
prepend: 'partials/',
module: 'project'
},
src: 'public/partials/*.html',
dest: 'spec/javascripts/angular/helpers/templates.js'
}
},
watch: {
templates: {
files: ['public/partials/*.html'],
tasks: ['ngtemplates']
}
}
});
grunt.loadNpmTasks('grunt-angular-templates');
grunt.loadNpmTasks('grunt-contrib-watch');
};
I solved the same problem in a slightly different way than the chosen solution.
First, I installed and configured the ng-html2js plugin for
karma. In the karma.conf.js file :
preprocessors: {
'path/to/templates/**/*.html': 'ng-html2js'
},
ngHtml2JsPreprocessor: {
// you might need to strip the main directory prefix in the URL request
stripPrefix: 'path/'
}
Then I loaded the module created in the beforeEach.
In your Spec.js file :
beforeEach(module('myApp', 'to/templates/myTemplate.html'));
Then I used $templateCache.get to store it into a variable.
In your Spec.js file :
var element,
$scope,
template;
beforeEach(inject(function($rootScope, $compile, $templateCache) {
$scope = $rootScope.$new();
element = $compile('<div my-directive></div>')($scope);
template = $templateCache.get('to/templates/myTemplate.html');
$scope.$digest();
}));
Finally, I tested it this way.
In your Spec.js file:
describe('element', function() {
it('should contain the template', function() {
expect(element.html()).toMatch(template);
});
});
To load the template html dynamically into $templateCache you could just use html2js karma pre-processor, as explained here
this boils down to adding templates '.html' to your files in the conf.js file
as well
preprocessors = {
'.html': 'html2js'
};
and use
beforeEach(module('..'));
beforeEach(module('...html', '...html'));
into your js testing file
if you're using Karma, consider using karma-ng-html2js-preprocessor to pre-compile your external HTML templates and avoid having Angular try to HTTP GET them during test execution. I struggled with this for a couple of ours - in my case templateUrl's partial paths resolved during normal app execution but not during tests - due to differences in app vs. test dir structures.
If you are using the jasmine-maven-plugin together with RequireJS you can use the text plugin to load the template content into a variable and then put it in the template cache.
define(['angular', 'text!path/to/template.html', 'angular-route', 'angular-mocks'], function(ng, directiveTemplate) {
"use strict";
describe('Directive TestSuite', function () {
beforeEach(inject(function( $templateCache) {
$templateCache.put("path/to/template.html", directiveTemplate);
}));
});
});
If you use requirejs in your tests then you can use the 'text' plugin to pull in the html template and put it in the $templateCache.
require(["text!template.html", "module-file"], function (templateHtml){
describe("Thing", function () {
var element, scope;
beforeEach(module('module'));
beforeEach(inject(function($templateCache, $rootScope, $compile){
// VOILA!
$templateCache.put('/path/to/the/template.html', templateHtml);
element = angular.element('<my-thing></my-thing>');
scope = $rootScope;
$compile(element)(scope);
scope.$digest();
}));
});
});
I resolve this issue with compiling all templates to templatecache.
I'm using gulp, you can find similar solution for grunt too.
My templateUrls in directives, modals looks like
`templateUrl: '/templates/directives/sidebar/tree.html'`
Add a new npm package in my package.json
"gulp-angular-templatecache": "1.*"
In gulp file add templatecache and a new task:
var templateCache = require('gulp-angular-templatecache');
...
...
gulp.task('compileTemplates', function () {
gulp.src([
'./app/templates/**/*.html'
]).pipe(templateCache('templates.js', {
transformUrl: function (url) {
return '/templates/' + url;
}
}))
.pipe(gulp.dest('wwwroot/assets/js'));
});
Add all js files in index.html
<script src="/assets/js/lib.js"></script>
<script src="/assets/js/app.js"></script>
<script src="/assets/js/templates.js"></script>
Enjoy!

Resources