I am preparing a boiler Angular Application that will use AMD (require.js). So far I got it working, but I have issues with services. How do I incorporate service?
This is Main.js:
require.config({
paths: {
// These are the core components - without which our application could not run
angular: '../lib/angular/angular',
angularResource: '../lib/angular/angular-resource',
// These are components that extend our core components
jquery: '../lib/jquery/jquery-1.8.2.min',
bootstrap: '../lib/bootstrap/js/bootstrap',
underscore: '../lib/underscore/underscore',
text: '../lib/require/text'
},
shim: {
'angular' : {'exports' : 'angular'},
'angular-resource' : {deps:['angular']},
'bootstrap': {deps:['jquery']},
'underscore': {exports: '_'}
},
priority: [
"angular"
],
urlArgs: 'v=1.1'
});
require( [
'angular',
'app',
'services/services',
'services/dateGetter',
'controllers/controllers',
'controllers/navbar',
'controllers/exampleTemplateController',
'filters/filters',
'directives/directives',
'routes'
], function(angular, app) {
angular.element(document).ready(function () {
angular.bootstrap(document, ['myApp']);
});
});
This is how I developed controller:
define([
'app',
'../helpers/exampleFile' //example of importing custom file.js in our controller.
],
function(app, exampleFile) {
'use strict';
return app.controller('exampleTemplateController', [
'$scope', 'dateGetter',
function ($scope ) {
$scope.sayHello = function(module) {
alert("Current Date: " + dateGetter.getCustomDate + " " + exampleFile(module.name));
}
}
]);
});
This is how I tried to develop service (failing miserably here):
define([
'app'
],
function(app) {
'use strict';
debugger;
return app.factory('dateGetter', [
'$scope',
function ($scope ) {
$scope.getCustomName = function() {
return "THIS IS MY NAME";
}
}
]);
});
I think I am close, but something eludes me.
Another question is, if I end up with lots of controllers and services, and we will... do I need to list each and everyone in main.js file (under require:)?
I got the service working now. It was my fault. Here it is now, and I can access it and use it.
define([
'app'
],
function(app) {
return app.factory('serviceExample', function ( ) {
return {
nameOfMethod: function () {
return "Is this working";
}
}
}
);
});
What I wonder now... Do I need to use required with Angular at all?
I have read many posts debating the same question, without concise answer.
I was thinking to use require, since we used it before, and this application will grow to be a large bugger anyway.
Opinions?
Related
I am trying to create a basic test to verify that I can create a controller or service.
My app is is the following directory
app/js/app.js
My controllers are in the following directory
app/js/controllers/
Here is my karma.conf.js file
files: [
'Scripts/jquery-2.1.1.min.js',
'Scripts/require.js',
'Scripts/angular.js',
'Scripts/angular-mocks.js',
{ pattern: 'app/js/*.js', included: false },
{ pattern: 'app/js/**/*.js', included: false },
{ pattern: 'app/js/**/**/*.js', included: false },
{ pattern: 'app/js/**/**/**/*.js', included: false },
{ pattern: 'test/specs/**/*.js', included: false },
'test/test-main.js',
],
// list of files to exclude
exclude: [
'app/js/main.js'
],
test-main.js
var testFiles = [];
for (var file in window.__karma__.files) {
console.log(file);
if (/Spec\.js$/.test(file)) {
testFiles.push(file);
}
}
requirejs.config({
paths: {
// External libraries
'jquery': '../../Scripts/jquery-2.1.1.min',
'angular': '../../Scripts/angular',
'angular-mocks': '../../Scripts/angular-mocks',
'ngRoute': '../../Scripts/angular-route.min',
'angular-animate': '../../Scripts/angular-animate.min',
'angular-cookies': '../../Scripts/angular-cookies.min',
},
baseUrl: '/base/app/js',
shim: {
'angular': {
exports: 'angular',
deps: ['jquery']
},
'angular-mocks': { exports: 'angular-mocks', deps: ['angular'] },
'angular-animate': { exports: 'angular-animate', deps: ['angular'] },
'ngRoute': { exports: 'ngRoute', deps: ['angular'] },
'angular-cookies': { exports: 'angular-cookies', deps: ['angular'] },
},
// dynamically load all test files
deps: testFiles,
// we have to kickoff jasmine, as it is asynchronous
callback: window.__karma__.start
});
I added this line...
console.log(file)
To make sure the file was loaded into
window.__karma__.files
and it is.
The test lives in test/specs/
define(['angular', 'angular-mocks'], (angular, ngMock: ng.IMockStatic) => {
var module = ngMock.module;
var inject: (...fns: Function[]) => any = ngMock.inject;
describe("Create an Application", function () {
var scope, q, routeParams;
var location;
var app;
beforeEach(function () {
app = angular.module('App', []);
inject(function ($rootScope, $q: ng.IQService, $location: ng.ILocationService) {
scope = $rootScope;
q = $q;
routeParams = {};
location = $location;
});
});
it('Test Application Created', function () {
expect(true).toBe(true);
});
});
});
My app file looks like this....
import angular = require('angular');
import angularRoute = require('angular-route');
import angularAnimate = require('angular-animate');
import ds = require('services/DataService');
var app = angular.module('app', [
'ngRoute',
'ngAnimate',
'ngCookies',
'kendo.directives',
'breeze.angular',
'ui.bootstrap'
]);
export = app;
the error that i get when I try to run the test is
failed to instantiate module app due to: Module 'app' is not available! You either misspelled the module name or forgot to load it.
I am assuming it is not loading but not sure how I can tell. Is this the best way to accomplish my testing?
Any help is greatly appreciated!
Thanks so much!
If you are using Karma with Jasmine, you may be having the problem of Jasmine being too eager.
This can be fixed by making two changes, originally described in an article I wrote when I was doing a similar thing, Combining TypeScript, Jasmine and RequireJS.
In boot.js you need to comment out the env.execute(); method call:
window.onload = function() {
if (currentWindowOnload) {
currentWindowOnload();
}
htmlReporter.initialize();
//env.execute();
};
This is because the window.onload event can happen before require.js has loaded everything.
Once you have loaded the modules you need, you can then call:
// Hai Jasmine - ready to go!
jasmine.getEnv().execute();
This is what it gets compiled with your code and the -m amd switch
describe("Create an Application", function () {
var scope, q, routeParams;
var controller, location;
var route;
beforeEach(function () {
module('app', ['ngRoute', 'ngAnimate', 'ngCookies', 'kendo.directives', 'breeze.angular', 'ui.bootstrap']);
inject(function ($rootScope, $q, $location) {
scope = $rootScope;
q = $q;
routeParams = {};
location = $location;
});
// controller = new controller.ApplicationService(q, scope, route, location, _datasvc, _searchsvc);
});
it('Test Application Created', function () {
expect(true).toBe(true);
});
});
is not a module, and its synchronous
But. in a module works
define(['angular','angular-mocks'],(angular,ngMock:ng.IMockStatic)=>{
var module = ngMock.module;
var inject: (...fns: Function[])=>any = ngMock.inject;
describe("Create an Application", function () {
var scope, q, routeParams;
var location;
var app;
beforeEach(function () {
app = angular.module('App', []);
inject(function ($rootScope, $q: ng.IQService, $location: ng.ILocationService) {
scope = $rootScope;
q = $q;
routeParams = {};
location = $location;
});
});
it('Test Application Created', function () {
expect(true).toBe(true);
});
});
});
Or alternatively in a more TypeScripty w/ES6esque way :
import ngMock = require('angular-mocks');
import angular = require('angular');
var module = ngMock.module;
var inject: (...fns: Function[])=>any = ngMock.inject;
describe("Create an Application", function () {
var scope, q, routeParams;
var location;
beforeEach(function () {
var app = angular.module('app', []);
inject(function ($rootScope, $q: ng.IQService, $location: ng.ILocationService) {
scope = $rootScope;
q = $q;
routeParams = {};
location = $location;
});
});
it('Test Application Created', function () {
expect(true).toBe(true);
});
});
check that you are loading angular as in a script tag
'files:[Scripts/angular.js',... ,
that's why you get
WARNING:tried load angular more than once
Put it in a { pattern: 'Scripts/angular.js', included: false ...],
you need to work out the requirejs.confg until the deps chain are satisfied. in order., and thats one of the reasons why you set the shim config with
'angular-mocks': { exports: 'angular-mocks', deps: ['angular'] },
so when requiring angular-mocks angular is required too ,
have a look at [this github repo](https://github.com/tnajdek/angular-requirejs-
seed),
...And once you have an instance attached to angular.module /mock.module in your spec , check Steve's answer again ,
If you can't find boot.js , you might be using jasmine bundled with the Karma adapter. in that case it should work given you have sorted out the require test-maiin.js / config / "NG_DEFER_BOOTSTRAP!" /resumeBootstrap story
Intro:
I recognize that this is really deep and I won't probably get any help here but I have exhausted what seems like the entire Internet for a solution.
The short of it is I want to make my ui-router dynamic, and with that the controllers, WHILE USING REQUIREJS.
I know Dan Wahlin has something similar but he hard codes his controllers in his main.js file and I'd much prefer keep everything related to $state.routes come from a DB, ie. template & controller.
This is the error I am getting:
Error: [ng:areq] Argument 'MyDashboardCtrl' is not a function, got
undefined
http://errors.angularjs.org/1.2.9/ng/areq?p0=MyDashboardCtrl&p1=not%20aNaNunction%2C%20got%20undefined
I know the problem is that Dashboard controller is not being cached even though it is in the ui-router as the default route.
While the Dashboard.html is appearing, via the ui-view="container", the controller is just not being cached.
main.js:
require.config({
baseUrl: "/app",
paths: {
'app' : '/app/app',
'jquery': '/vendor/jquery/dist/jquery.min',
'angular': '/vendor/angular/angular',
'angular-bootstrap': '/vendor/angular-bootstrap/ui-bootstrap-tpls.min',
'angular-breeze': '/vendor/breeze.js.labs/breeze.angular',
'angular-cookies': '/vendor/angular-cookies/angular-cookies.min',
'angular-resource': '/vendor/angular-resource/angular-resource.min',
'angular-route': '/vendor/angular-route/angular-route.min',
'angular-sanitize': '/vendor/angular-sanitize/angular-sanitize.min',
'angular-touch': '/vendor/angular-touch/angular-touch.min',
'angular-ui-router':'/vendor/angular-ui-router/release/angular-ui-router.min',
'angular-ui-tree':'/vendor/angular-ui-tree/angular-ui-tree.min',
'angularAMD': '/vendor/angularAMD/angularAMD.min',
'bootstrap':'/vendor/bootstrap/dist/js/bootstrap.min',
'breeze':'/vendor/breeze/breeze.min',
'breeze-directives':'/vendor/breeze.js.labs/breeze.directives',
'highcharts':'/vendor/highcharts/highcharts',
'highchartsng':'/vendor/highcharts/highcharts-ng',
'moment': '/vendor/moment/min/moment.min',
'ngload': '/vendor/ng-load/ng-load',
'q': '/vendor/q/q',
'spin': '/vendor/javascripts/jquery.spin',
'toastr': '/vendor/toastr/toastr.min',
'tweenMax':'/vendor/greensock/src/minified/TweenMax.min',
'underscore':'/vendor/underscore/underscore.min'
},
// Add angular modules that does not support AMD out of the box, put it in a shim
shim: {
angular: {
exports: "angular"
},
'angularAMD': ['angular'],
'angular-ui-router':['angular'],
'highchartsng': {deps:['highcharts']},
'bootstrap': {deps:['jquery']},
'toastr': {deps:['jquery']}
},
// kick start application
deps: ['app'] });
app.js:
define(['angularAMD', 'angular-ui-router', 'config/common',
'layout/services/dBoardSvc', 'localize/LangSettings.Svc'],
function (angularAMD, common, dBoardSvc, LangSettingsSvc) {
var $stateProviderRef = null;
var $urlRouterProviderRef = null;
var app = angular.module("anuApp", ['ui.router'])
/* #ngInject */
.config(function ($locationProvider, $urlRouterProvider, $stateProvider) {
$urlRouterProviderRef = $urlRouterProvider;
$stateProviderRef = $stateProvider;
$locationProvider.html5Mode(false);
$urlRouterProviderRef.otherwise('/')
})
/* #ngInject */
.run(function($q, $rootScope, $state, $window, common, dBoardSvc, LangSettingsSvc){
// set initial current language
LangSettingsSvc.currentLang = LangSettingsSvc.languages[0];
dBoardSvc.all().success(function (data) {
var startUp = undefined;
angular.forEach(data, function (value) {
var tmp = 6;
angular.forEach(value.viewGroups, function (viewGroup, key) {
angular.forEach(viewGroup.viewStates, function (viewState, key) {
var viewStateUrl = undefined;
if (viewState.isStartUp == true && startUp == undefined) {
startUp = viewState.name;
}
if (viewState.url != '0') {
viewStateUrl = viewState.name + "/:moduleId";
}
var state = {
"url": viewStateUrl,
"parent": viewState.parentName,
"abstract": viewState.isAbstract,
"views": {}
};
angular.forEach(viewState.views, function (view) {
state.views[view.name] = {
controller: view.controllerName,
templateUrl: common.appRoot + view.templateUrl + '.html',
controllerUrl: common.appRoot + view.controllerUrl + '.js'
};
});
$stateProviderRef.state(viewState.name, angularAMD.route(state));
--------->>>>>>>>>>>>>>>>> **console.log(state);**
});
});
});
$state.go(startUp);
});
});
// Bootstrap Angular when DOM is ready
angularAMD.bootstrap(app);
return app; });
DashboardCtrl:
define(['app'], function (app) {
'use strict';
app.register.controller('MyDashboardCtrl', MyDashboardCtrl);
/* #ngInject */
function MyDashboardCtrl($scope){
$scope.pageTitle = 'MyDashboardCtrl';
};
});
Conclusion:
Could this be some time of resolve issue? I am literally grasping for straws here and any help would be appreciated.
The problem was with my controller, below is the correct approach:
define(['angularAMD'], function (app) {
'use strict'
/* #ngInject */
app.controller('MyDashboardCtrl', MyDashboardCtrl);
function MyDashboardCtrl($scope) {
$scope.pageTitle = 'This controller now loads dynamically';
};
// MyDashboardCtrl.$inject = ['$scope'];
});
NOTHING is hard coded in my app short of vendor files. My controllers, my views and my services are all dynamic & authorization based!!!
I hope this helps others because the implications of being able to utilize requirejs with dynamic routes means that not only does one :
Minimize the javaScript to only that which the view needs, but by coupling it with dynamic views & a login,
One can effectively hide all client side scripts that a respective user may not have authority to acccess.
I just wish examples were more than 'Hello World' these days ;(
In my AngularJS app, using Yeoman, when minifying my app I have this error :
Uncaught Error: [$injector:unpr] Unknown provider: aProvider <- a <- $http <- AuthenticationService
that I obviously do not have before minifying.
Here is the definition of my service in a separate runner.js file :
angular.module('myApp').run(['$rootScope', 'AuthenticationService', 'FlashService', 'SessionService', function ($rootScope, AuthenticationService, FlashService, SessionService) {
//some code
}]);
I thought of course about the typical Injection error when minifying but I am struggling to see what is wrong in my code...
UPDATE
My AutenticationService :
angular.module('myApp').factory("AuthenticationService", ['$http', '$rootScope', '$sanitize', 'SessionService', 'FlashService', 'SETTINGS', function($http, $rootScope, $sanitize, SessionService, FlashService, SETTINGS) {
var cacheSession = function() {
SessionService.set('authenticated', true);
};
var uncacheSession = function() {
SessionService.unset('authenticated');
SessionService.unset('user');
};
var loginError = function(response) {
FlashService.show('warning', response.flash);
};
var loginSuccess = function(response) {
SessionService.set('user', JSON.stringify(response));
FlashService.clear();
};
var logoutSuccess = function(response) {
FlashService.show('success', response.flash);
};
var sanitizeCredentials = function(credentials) {
return {
email: $sanitize(credentials.email),
password: $sanitize(credentials.password)
};
};
return {
login: function(credentials) {
var login = $http.post(SETTINGS.urlBackend+"/auth/login", sanitizeCredentials(credentials));
login.success(cacheSession);
login.success(loginSuccess);
login.error(loginError);
return login;
},
logout: function() {
var logout = $http.get(SETTINGS.urlBackend+"/auth/logout");
logout.success(uncacheSession);
logout.success(logoutSuccess);
logout.error(loginError);
return logout;
},
isLoggedIn: function() {
var checked = $http.get(SETTINGS.urlBackend+"/auth/check");
return (checked && SessionService.get('authenticated'));
}
};
}]);
Try setting mangle: false in the Uglify configuration in your Gruntfile.js:
grunt.initConfig({
// ...
uglify: {
options: {
mangle: false
}
}
});
I've had this happen when using certain packages from Bower. I believe some of the Angular UI suite of tools weren't compatible, for some reason.
I highly recommend an alternative approach to manually setting up the angular minification work around - the wrapping of the "module" - controller, service, factory - in the [] brackets.
Use the ng-min module! It's written by the guys as Angular - namely Brian Ford. But most important it removes this complication in writing Angular modules, they become clean and readable again and the ng-min does the hard work of fixing minification issues.
I know it's not an answer to you question, but it might be a solution to the problem you are facing in general.
// Allow the use of non-minsafe AngularJS files. Automatically makes it // minsafe compatible so Uglify does not destroy the ng references
ngmin: {
dist: {
files: [
{
expand: true,
cwd: '.tmp/concat/scripts',
src: '*.js',
dest: '.tmp/concat/scripts'
}
]
}
},
I've been trying to practice on backbone.js, I found one tutorial online which I was trying to follow (http://backbonetutorials.com/what-is-a-router/) but unfortunately i have problem reaching to my view through the router.
Below is my code
main.js
requirejs.config({
// create local alias for package
paths: {
l : 'my/vehicle',
underscore : 'vendors/underscore',
jqueryui : 'vendors/jquery-ui/js/jquery-ui-1.9.0.custom.min',
backbone : 'vendors/backbone',
bootstrap : 'vendors/bootstrap'
},
shim: {
backbone: {
deps: ["underscore", "jquery"],
exports: "Backbone"
},
underscore: {
exports: "_"
}
}
})
require(['../../config'], function(core){
require(["l/app"], function(App) {
'use strict';
App.initialize();
});
});
app.js
define(["jquery", "backbone", "l/router"], function($, Backbone, Router) {
var initialize = function(){
// Pass in our Router module and call it's initialize function
Router.initialize();
}
return {
initialize: initialize
};
});
router.js
define([
'jquery',
'underscore',
'backbone',
'l/views/BrowseVehicle'
], function($, _, Backbone, BrowseVehicleView){
var AppRouter = Backbone.Router.extend({
routes: {
// Define some URL routes
'/browse' : 'showVehicleBrowse',
// Default
'*actions' : 'defaultAction'
}
});
var initialize = function(){
var app_router = new AppRouter;
app_router.on('showVehicleBrowse', function(){
// Call render on the module we loaded in via the dependency array
console.log('am here');
var BrowseVehicleView = new BrowseVehicleView();
BrowseVehicleView.render();
});
app_router.on('defaultAction', function(actions){
// We have no matching route, lets just log what the URL was
console.log('No route:', actions);
});
Backbone.history.start();
};
return {
initialize: initialize
};
});
views/BrowseVehicle.js
define([
'jquery',
'underscore',
'backbone'
], function($, _, Backbone){
var BrowseVehicleView = Backbone.View.extend({
el: $('#vehicle-browse-form'),
render: function(){
// Using Underscore we can compile our template with data
console.log('I reached vehicle browse form');
}
});
// Our module now returns our view
return BrowseVehicleView;
});
There is no error on loading the code, console.log is not printing anything in the view nor in the router inside the routed function. I tried to access my urls using URL/#/browse but not getting the console.log statement.
Can anyone please advise?
In the routes {} definition remove the forward slash in front of browse.
Let's say my app works but I love to learn and to find the best way of doing things.
I really appreciate this post about Reducing Backbone Routers To Nothing More Than Configuration.
And the following bbclonemail which is not using requires.
Actually my implementation is a monolithic block (app.js, router.js).
Here's my questions:
1) What should the router module router.js return?
2) How should I remove The Callback Functions from router.js ?
3) What should the app module app.js return?
4) How should I decouple the app.js in many others apps (for example: main, tasks, projects)
app.js
// app.js
define([
'router'
// some modules
],
function (router, Backbone, HeaderView)
{
"use strict";
var myApp = new Backbone.Marionette.Application();
myApp.addRegions({
header: '#header',
sidebar: '#sidebar',
mainColumn: '#main-column',
rightColumn: '#right-column'
});
myApp.initHeader = function () {
var headerView = new HeaderView();
myApp.header.show(headerView);
}
// many others many views
myApp.start();
myApp.initialize = function() {
router.initialize();
Backbone.history.start();
}
return myApp;
});
router.js
// router.js
define([
// some modules
],
function (Backbone)
{
"use strict";
var AppRouter = Backbone.Marionette.AppRouter.extend({
routes: {
tasks: 'tasks',
projects: 'projects',
// many others keys/values
'*defaults': 'home'
},
getApp: function ()
{
var mainApp;
require(['js/app'], function (app) {
mainApp = app;
});
return mainApp;
},
home: function()
{
var app = this.getApp();
app.initHeader();
app.initSidebar();
app.initTaskDetails();
},
// many others callbacks
});
var initialize = function() {
new AppRouter;
};
return {
initialize: initialize
};
});
For the Router part you should make like this:
router.js
// router.js
define([
'rooterController'
// some modules
],
function (Backbone, rooterController)
{
"use strict";
var AppRouter = Backbone.Marionette.AppRouter.extend({
routes: {
tasks: 'tasks',
projects: 'projects',
// many others keys/values
'*defaults': 'home'
}
});
return new AppRouter({constroller: rooterController})
});