AngularJS - Minifying modules with gulp - angularjs

I am trying to set up a structure so I can minify/uglify all of my angular code.
I tried to copy the angular module structure from the Angle Bootstrap Theme
It works fine when all the unmified js files are loaded but when I try to minify all it ends up not finding the modules.
I am getting this error: [$injector:nomod] Module 'app.core' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
This is a MVC 5.2.3 project and the structure of the angular files are like this:
Scripts/angular/app.module.js
Scripts/angular/modules/core/core.config.js
Scripts/angular/modules/core/core.module.js
Scripts/angular/modules/module1/module1.controller.js
Scripts/angular/modules/module1/module1.module.js
So app.module.js looks like this:
(function() {
'use strict';
angular
.module('myApp', [
'app.core',
'app.module1',
...
]);
})();
core.config.js:
(function () {
'use strict';
angular
.module('app.core')
.config(coreConfig);
coreConfig.$inject = ['$controllerProvider', '$compileProvider', '$filterProvider', '$provide', '$animateProvider'];
function coreConfig($controllerProvider, $compileProvider, $filterProvider, $provide, $animateProvider) {
var core = angular.module('app.core');
core.controller = $controllerProvider.register;
core.directive = $compileProvider.directive;
core.filter = $filterProvider.register;
core.factory = $provide.factory;
core.service = $provide.service;
core.constant = $provide.constant;
core.value = $provide.value;
core.config(function (paginationTemplateProvider) {
paginationTemplateProvider.setPath(window.rootUrl + 'scripts/plugins/angular-pagination/dirPagination.tpl.html');
});
$animateProvider.classNameFilter(/^((?!(ng-no-animation)).)*$/);
}
})();
core.module.js:
(function() {
'use strict';
angular
.module('app.core', [
'ngSanitize',
'ngRoute',
'ngAnimate',
'ngResource',
'ngStorage',
'ui.bootstrap'
]);
})();
And all the modules have this structure:
module1.controller.js:
(function () {
'use strict';
angular
.module('app.note')
.controller('module1', module1);
module1.$inject = ['$scope', '$localStorage'];
function module1($scope, $localStorage) {
.....
};
})();
and the module1.module.js:
(function () {
'use strict';
angular
.module('app.module1', []);
})();
gulpfile.js
/// <binding Clean='clean' />
"use strict";
var gulp = require("gulp"),
rimraf = require("rimraf"),
concat = require("gulp-concat"),
cssmin = require("gulp-cssmin"),
uglify = require("gulp-uglify");
var paths = {
webroot: "./"
};
paths.js = paths.webroot + "scripts/angular/**/*.js";
paths.minJs = paths.webroot + "scripts/angular/**/*.min.js";
..
gulp.task("min:js", function () {
return gulp.src([paths.js, "!" + paths.minJs], { base: "." })
.pipe(concat(paths.concatJsDest))
.pipe(uglify())
.pipe(gulp.dest("."));
});
..

probably is an order problem.
You have a good naming conventio so you can order your input stream taking the *.module.js files first.
you have to include
var order = require("gulp-order");
than after the gulp.src you have to order your input the risult should be likethis
gulp.task("min:js", function () {
return gulp.src([paths.js, "!" + paths.minJs], { base: "." })
.pipe(order([
'**/*.module.js',
'**/*.js'
]))
.pipe(concat(paths.concatJsDest))
.pipe(uglify())
.pipe(gulp.dest("."));
});
as you can see the order take an array of pattern.
Maybe the pattern are not perfect for your case but it should work.
hope this helps

Related

Angular test failing to instantiate module with Jasmine and Karma

I have setted up a test environment with Angular 1.6.6, ng-mock 1.6.6, Jasmine and Karma. But even with the easiest test I'm getting a [$injector:modulerr] when trying to inject the deloreanApp module
Failed to instantiate module deloreanApp due to: Error:
[$injector:nomod]
Theoretically there aren't tipo errors and Angular and ng-mock versions matches.
My files are:
app.js
(function () {
"use strict";
// initialize Angular
angular.module('deloreanApp', ['deloreanApp.controllers', 'deloreanApp.services']);
angular.module('deloreanApp.controllers', []);
angular.module('deloreanApp.services', []);
})();
controllers.js
(function () {
"use strict";
function deloreanController($scope){
$scope.sum = function(a,b){
return a + b;
}
}
angular.module('deloreanApp.controllers', []).controller('DeloreanController', ['$scope', deloreanController] );
})();
DeloreanController.test.js
describe('calculator', function () {
beforeEach(module('deloreanApp'));
var $controller;
beforeEach(inject(function (_$controller_) {
$controller = _$controller_;
}));
describe('sum', function () {
it('1 + 2 should equal 3', function () {
var result = 3;
expect(result).toBe(3);
});
});
});
And part of my karma.conf.js file:
// list of files / patterns to load in the browser
files: [
'lib/angular.min.js',
'lib/angular-mocks.js',
'js/app.js',
'js/controllers.js',
'tests/DeloreanController.test.js'
],
Try this,
(function () {
"use strict";
angular.module('deloreanApp.controllers', []);
angular.module('deloreanApp.services', []);
// initialize Angular
angular.module('deloreanApp', ['deloreanApp.controllers', 'deloreanApp.services']);
})();

Angular app test with Karma

I'm new to Jasmine tests with Karma. I'm getting following error while executing the tests.
Here are my files.
message.service.js // Service file
(function() {
'use strict';
angular.module("app").factory('MessageService', [MessageService]);
function MessageService() {
var service = {};
//var gui = require('nw.gui');
service.alert = function() {
//gui.Window.open('/');
//$window.open('/message');
return 'hello';
};
return service;
};
})(); // IIFE
message.service.spec.js // Service test file
describe('Message Service', function() {
var messageService;
beforeEach(angular.mock.module('app'));
beforeEach(inject(function(_MessageService_) {
messageService = _MessageService_;
}));
it('should exist', function() {
expect(messageService).toBeDefined();
});
it('should return hello', function() {
expect(messageService.alert()).toEqual('hello');
});
});
app.module.js // Main app file
(function() {
'use strict';
angular.module("app", ['ngRoute', 'ui.bootstrap', 'ui.router']);
/*angular.module("app").run(['$rootScope', 'StartupService', function($rootScope, startupService){
startupService.init($rootScope);
}]);*/
})(); // IIFE
If I change following line in message.service.js
angular.module("app").factory('MessageService', [MessageService]);
to
angular.module("app",[]).factory('MessageService', [MessageService]);
Then tests work fine but application will not work(due to re initiation of module I guess). How can I make my app to work for both tests and normal app execution? Appreciate your help
It was my mistake. I haven't included the js file in Karma config related to the ui.bootstrap module. After that it worked fine.
For anyone who gets such error check whether you have included all the dependency files on Karma config, what you have defined in main app.
ex: assume your main app file is like below.
angular.module("app", ['ngRoute', 'ui.bootstrap', 'ui.router']);
Then your Karma config file should look like this
files: [
'assets/lib/angular/angular.min.js',
'assets/lib/angular-ui-router/angular-ui-router.min.js',
'assets/lib/angular-bootstrap/ui-bootstrap-tpls-1.3.2.min.js'
],
Thanks for your help.

How to access angular app module that is defined in self invoke?

I want use unit test in angular, but the webapp that I working on it is created on its owe structure. for example :
(function() {
'use strict';
angular
.module('app', [
'ngAnimate',
'ui.load',
'ui.jp',
'oc.lazyLoad'
]);})();
And one of its controllers :
(function() {
'use strict';
angular
.module('app')
.controller('EditorCtrl', EditorCtrl);
function EditorCtrl($scope) {
var vm = $scope;
vm.options = {
toolbar: [
['style', ['bold', 'italic', 'underline', 'clear']],
]
};
}})();
And I have no idea how to use unit test to this app because my test cant find controller.
This is my controller and test :
(function () {
'use strict';
angular
.module('app')
.controller('DashboardCtrl', dashboard);
describe('test dashboard', function () {
beforeEach(module('DashboardCtrl'));
var $controller;
beforeEach(inject(function (_$controller_) {
$controller = _$controller_;
}));
describe('sum', function () {
it('1 + 1 should equal 2', function () {
var $scope = {};
var controller = $controller('DashboardCtrl', {$scope: $scope});
$scope.x = 1;
$scope.y = 2;
$scope.sum();
expect($scope.z).toBe(3);
});
it('z should default to zero', function () {
var $scope = {};
var controller = $controller('DashboardCtrl', {$scope: $scope});
expect($scope.z).toBe(0);
});
});
});
function dashboard($scope) {
$scope.name = 'Dashboard';
$scope.z = 0;
$scope.sum = function () {
$scope.z = $scope.x + $scope.y;
};
}
})();
And in karma test show me this error :
Error: [$injector:modulerr] Failed to instantiate module DashboardCtrl due to:
Error: [$injector:nomod] Module 'DashboardCtrl' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
That indicate that cant find "DashboardCtrl" controller.
the problem solved by add all modules in files options of karma.conf.js
the point is event lake of one of modules that are injected is main module that here is app stop your test, so addition to include your controller you need to add all module are mentioned.
When unit testing use $controller for getting a handle on a controller instance https://docs.angularjs.org/api/ng/service/$controller like var ctrlToTest = $controller('EditorCtrl')(newTestScope); To create a new test scope you can use var newTestScope = $rootScope.$new()

how to inject factory into another factory in Angular

I have two modules and factories in both, and I need to implement factory from the first module into another module.
angular.module('APIs', [])
.value ("myValue" , "12345")
.factory('apiUrl',['config','url',apiUrl])
function apiUrl(config,url){
}
angular.module('users.service', ['APIs'])
.factory('userService',['myValue',userService])
function userService(apiUrl,myValue){
//login function
function login(){
console.log('myValue',myValue)
console.log('loginUrl',apiUrl)
}
return {
login:login
}
}
notice: no problem when I inject myValue, but the problem in APIs Factory
and my log:
Error: [$injector:unpr] http://errors.angularjs.org/1.5.0/$injector/unpr?p0=urlProvider%20%3C-%20url%20%3C-%20apiUrl%20%3C-%20userService
at Error (native)
and sorry for my English.
I would do it like this:
If you create factory use angular.module('app').factory(). If you create service use angular.module('app').service()
Always try to have same module name. It is easier later, when you have big application, because of dependency injection.
Try to keep files separately, and concatenate it later, for example using gulp gulp-concat
Try to keep all you configuration in app.js file and, when you concatenating file, remember, this file should be on top.
I would keep values and constants in app.js file or would create new file like factory or service and include it, same as I injected it below.
app.js
(function () {
'use strict';
var app = angular.module('app', [
// other module
]);
app.value('myValue', '12345');
app.constant('myConst', 'some_constant');
app.config(['$interpolateProvider', function ($interpolateProvider) {
$interpolateProvider.startSymbol('[[');
$interpolateProvider.endSymbol(']]');
}]);
}());
factory.js
(function () {
'use strict';
angular
.module('app')
.factory('apiUrlFactory', apiUrlFactory);
apiUrlFactory.$inject = [];
function apiUrlFactory() {
var self = this;
self.url = 'some_url';
return self;
}
}());
service.js
(function () {
'use strict';
angular
.module('app')
.service('userService', userService);
userService.$inject = ['apiUrlFactory', 'myValue'];
function userService(apiUrlFactory, myValue) {
var self = this;
self.login = function () {
console.log('myValue', myValue);
console.log('loginUrl', apiUrlFactory.url);
};
return self;
}
}());
If you have more questions, do not hesitate to contact with me. Also try angular style guide https://github.com/johnpapa/angular-styleguide It will help you a lot.

AngularJS filter in controller

I have this angular loop and controller:
<li class="list-group-item" ng-repeat="post in posts | filter: post._creator = getCurrentUser"></li>
and Controller:
$http.get('/api/posts').success(function(posts) {
$scope.posts = posts;
$scope.getCurrentUser = Auth.getCurrentUser()._id;
});
It is possible to inject the filter in the controller?
What is the best practice?
I created a dedicated file filers.js and put filters registration code into the file.
'use strict';
angular.module('ftFilters', [])
.filter('rub', function() {
return function (value) {
if (value === undefined || value === null){
return 'не задана';
}
return value.toFixed(2).toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1 ").replace('.','<small>,') + '</small> руб';
};
})
.filter('rudate', function($filter) {
var angularDateFilter = $filter('date');
return function(theDate) {
return angularDateFilter(theDate, 'dd.MM.yy HH:mm:ss');
}
});
then you must add this module into dependencies for your application, e.g.
'use strict';
// Init the application configuration module for AngularJS application
var ApplicationConfiguration = (function() {
// Init module configuration options
var applicationModuleName = 'ft';
var applicationModuleVendorDependencies = ['ngResource', 'ngCookies', 'ngAnimate', 'ngTouch', 'ngSanitize',
'ui.router', 'ui.bootstrap', 'ui.utils', 'vr.directives.slider', 'ya.treeview', 'ui.select', 'ftFilters' ];
// Add a new vertical module
var registerModule = function(moduleName) {
// Create angular module
angular.module(moduleName, []);
// Add the module to the AngularJS configuration file
angular.module(applicationModuleName).requires.push(moduleName);
};
return {
applicationModuleName: applicationModuleName,
applicationModuleVendorDependencies: applicationModuleVendorDependencies,
registerModule: registerModule
};
})();
after this you can use it everywhere

Resources