define in require function (requireJS) - angularjs

(Using requireJS, angularJS, angularAMD)
When in my main.js file I have:
require.config({
baseUrl: "js",
paths: {
'angular': 'libs/angularjs/angular.min',
'angularAMD': 'libs/angularjs/angularAMD.min'
},
shim: {
'angularAMD': ['angular']
}
});
define(['angularAMD'], function (angularAMD) {
var app = angular.module("app", []);
app.controller("testCtrl", function ($scope) {
$scope.message = "udało się!";
});
angularAMD.bootstrap(app);
return app;
});
everything works fine.
But when I cut config part to other file I got errors:
main.js:
require(['common'], function (common) {
define(['angularAMD'], function (angularAMD) {
var app = angular.module("app", []);
app.controller("testCtrl", function ($scope) {
$scope.message = "udało się!";
});
angularAMD.bootstrap(app);
return app;
});
});
common.js
require.config({
baseUrl: "js",
paths: {
'angular': 'libs/angularjs/angular.min',
'angularAMD': 'libs/angularjs/angularAMD.min'
},
shim: {
'angularAMD': ['angular']
}
});
Can I use define in require function? If not how to include common config first and then use define?

I'm assuming your main.js file is what you give to the data-main attribute on the <script> tag that loads RequireJS or that it is the main entry point of your application. Change main.js to this:
require(['common'], function (common) {
require(['app']);
});
And create an app.js module in a location where your code can readily load it:
define(['angularAMD'], function (angularAMD) {
var app = angular.module("app", []);
app.controller("testCtrl", function ($scope) {
$scope.message = "udało się!";
});
angularAMD.bootstrap(app);
return app;
});
Anything that needs access to the value of app you create in this module can just require the app module.
The code you have in the question defines a module but it does so asynchronously. By the time the call to define happens, RequireJS has already finished loading main. As far as it is concerned, main is done. So what name should it give to the defined module?

Related

Controllers and Directives in Angular + RequireJS

In this plunk I have a sample code running Angular + Angular UI Router + RequireJS. There are two pages, each with a corresponding controller. If you click on View 1, you should see a page that contains a directive.
When the page loads it throws the following exception:
Cannot read property 'controller' of undefined at at my-ctrl-1.js:3
meaning that app is undefined in my-ctrl-1.js even though I'm returning it in app.js. What's wrong with this code?
HTML
<ul class="menu">
<li><a href ui-sref="view1">View 1</a></li>
<li><a href ui-sref="view2">View 2</a></li>
</ul>
<div ui-view></div>
main.js
require.config({
paths: {
'domReady': 'https://cdnjs.cloudflare.com/ajax/libs/require-domReady/2.0.1/domReady',
'angular': 'https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.min',
"uiRouter": "https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.3.2/angular-ui-router"
},
shim: {
'angular': {
exports: 'angular'
},
'uiRouter':{
deps: ['angular']
}
},
deps: [
'start'
]
});
start.js
define([
'require',
'angular',
'app',
'routes'
], function (require, angular) {
'use strict';
require(['domReady!'], function (document) {
angular.bootstrap(document, ['app']);
});
});
app.js
define([
'angular',
'uiRouter',
'my-ctrl-1',
'my-ctrl-2',
'my-dir-1'
], function (angular) {
'use strict';
console.log("app loaded");
return angular.module('app', ['ui.router']);
});
my-ctrl-1.js
define(['app'], function (app) {
'use strict';
app.controller('MyCtrl1', function ($scope) {
$scope.hello = "Hello1: ";
});
});
The problem is that you have a circular dependency between app.js and my-ctrl-1.js. When RequireJS encounters a circular dependency, the references it passes to the modules' factories are going to be undefined. There are many ways to solve the issue. One simple way that would work with the code you show could be to change my-ctrl-1.js to:
define(function () {
'use strict';
return function (app) {
app.controller('MyCtrl1', function ($scope) {
$scope.hello = "Hello1: ";
});
};
});
And in app.js:
define([
'angular',
'my-ctrl-1',
'my-ctrl-2',
'my-dir-1',
'uiRouter',
], function (angular, ctrl1) {
'use strict';
console.log("app loaded");
var app = angular.module('app', ['ui.router']);
ctrl1(app);
return app;
});
Presumably, you'll have to do the same thing with your other controler.
The documentation has a section on the topic of circular dependencies and other methods to handle them.

RequireJS & Toaster

I have a problem implementing Toaster into my demoApp which uses RequireJS. Here some code:
(function () {
require.config({
paths: {
'angular': 'bower_components/angular/angular',
'jquery': 'bower_components/jquery/dist/jquery',
'toaster': 'bower_components/toaster/toaster'
},
shim: {
angular: {
deps: ['jquery'],
exports: 'angular'
},
toaster: {
deps: ['angular', 'jquery'],
exports: 'toaster'
}
}
});
require([
'angular',
'app',
'toaster',
'jquery'
],
function (angular, app, toaster) {
'use strict';
// toaster is undefined. I add it here just for a check. <<<<<<
angular.bootstrap(angular.element('body')[0], ['myApp']);
});
})();
This is main.js and toaster is undefined where I wrote the comment near the end. The file is loaded as I can see it at the Sources tab in the console.
In addition, wherever I want to use toaster, it is undefined. Here some code from the same demo app:
First case:
define(['somefile', 'toaster'], function (someModule, toaster) {
'use strict';
// toaster is undefined
});
Second case (John Papa Angular Style Guide):
define(['somefile', 'toaster'], function (someModule) {
'use strict';
someModule.controller('NewController', NewController);
NewController.$inject = ['someDeps', 'toaster'];
function NewController(someDeps, toaster) {
// angular.js:13424 Error: [$injector:unpr]
// Unknown provider: toasterProvider <- toaster <- NewController
}
});
Here's what I'm using:
Angular: 1.5.3
RequireJs: 2.2.0
Toaster: 2.0.0
Can anyone tell me what I'm doing wrong?
You have to distinguish between Angular modules and RequireJS modules. Toaster only registers an Angular module, no need to export anything in a RequireJS way.
shim: {
// ...
toaster: {
deps: ["angular", "jquery"]
}
}
Bootstrapping:
require(["angular", "app"], function (angular) {
// here, app.js is loaded in the DOM, so you can bootstrap Angular:
angular.bootstrap(angular.element("body")[0], ["myApp"]);
})
In your app.js:
define(["toaster" /* , ... */], function () {
// here, toaster.js is loaded in the DOM, so you can add the "toaster" Angular module in your Angular app dependencies:
return angular.module("myApp", ["toaster" /* , ... */]);
});
Anywhere else:
define(["app"], function (app) {
// as myApp depends on toaster, you can inject the toaster service the Angular way:
app.controller("MyController", ["toaster", function (toaster) {
// ...
}]);
});

how to load appCtrl dynamically with ng-controller attribute with dynamic controller (on the fly) concept

I have following setup for my project. Everything works fine.
In my Index.html I have two links (two $state). If I click on one of them, appropriate controller gets initiated dynamically as required. So dynamic concept works fine. But look at my index.html page below,
Index.html
<script src="~/Scripts/require.js" data-main="ANGULAR/main.js"></script>
<div data-ng-controller="appCtrl"> // only this line is not working and throwing an error saying appCtrl is not a function...
// Everything works fine. There is no problem at all. But look at the above line (data-ng-controller="appCtrl"). I know how can I initiate controller dynamically this way?
// When this line gets initiated it throws an error stating that appCtrl is not a function. I really don't know how to initiate appCtrl with this following setup.
<a ui-sref="dashboard">DASHBOARD</a><br /> //works fine
<a ui-sref="login">LOGIN</a> //works fine
<ui-view></ui-view> //works fine
</div>
main.js
require.config({
paths: {
"angular": "//localhost:59293/Scripts/angular",
"ui-router": "//localhost:59293/Scripts/angular-ui-router",
"ui-bootstrap": "//localhost:59293/Scripts/angular-ui/ui-bootstrap-tpls",
// "appCtrl":"//localhost:59293/ANGULAR/APP/appCtrl"
},
shim: {
"angular": {
exports: 'angular'
},
"ui-router": {
deps: ['angular']
},
"ui-bootstrap": {
deps: ['angular']
},
// "appCtrl": {
// deps: ['angular']
// }
}
});
define(
['angular',
'APP/app',
'APP/appCtrl' // Should I write this here ????????????????
], function (angluar, app) {
angular.bootstrap(document, ['MyApp'])
});
app.js looks like this,
define([
'angular',
'ui-router',
'ui-bootstrap',
], function (angular) {
var app = angular.module('MyApp', ['ui.router', 'ui.bootstrap']);
function lazy() {
var self = this;
this.resolve = function (controller) {
return {
ctrl: ['$q', function ($q) {
var defer = $q.defer();
require(['controllers/' + controller], function (ctrl) {
app.register.controller(controller, ctrl);
defer.resolve();
});
return defer.promise;
}]
};
};
this.$get = function () {
return self;
};
}
function config($stateProvider, $urlRouterProvider,
$controllerProvider, $compileProvider,
$filterProvider, $provide, lazyProvider) {
$urlRouterProvider.otherwise('/dashboard');
$stateProvider
.state("dashboard", {
url: "/dashboard",
controller: 'Dashboard/dashboardCtrl', // this works fine
controllerAs: 'vm',
templateUrl: 'ANGULAR/TEMPLATES/DASHBOARD/dashboard.html',
resolve: lazyProvider.resolve('Dashboard/dashboardCtrl')
})
.state("login", {
url: "/login",
controller: 'loginCtrl', //this works fine
controllerAs: 'vm',
templateUrl: 'ANGULAR/TEMPLATES/Login.html',
resolve: lazyProvider.resolve('loginCtrl')
})
;
app.register = {
controller: $controllerProvider.register,
directive: $compileProvider.directive,
filter: $filterProvider.register,
factory: $provide.factory,
service: $provide.service,
constant: $provide.constant
};
}
app.provider('lazy', lazy);
app.config(config);
return app;
});
appCtrl.js // I want this to work correctly.
//This controller doesn't get called with data-ng-controller attribute :(
// how and where should I add appCtrl.js reference as it is not defined in route config function? in main.js? if Yes, then how?
// I have commented code in main.js. please help and suggest.
define([
], function () {
console.log('appCtrl controller loaded');
ctrl.$inject = ['$http','$scope'];
function ctrl($http,$scope) {
this.message = '-- from a lazy controller.';
debugger;
$scope.myVar= "hello world"; // I want this value in HTML page.
};
return ctrl;
});
Please look at http://plnkr.co/edit/UDqaD7QKvgqtzgttXLHq?p=preview
but this is not working as mentioned... I just want to initiate appCtrl.js with ng-controller attribute dyanmically.
First, I've never seen a controller written like that. So that might be one of your problems. Another problem I see is that you are not declaring your controllers. Here's a plunker http://plnkr.co/edit/f343W3?p=preview
But here's the code. First, in your app.js
var app = angular.module('MyApp', [
'ui.router',
// Add your controller
'MyApp.controllers.appCtrl'
]);
Otherwise it angular won't know what you mean.
Second, your controller:
(function (){
'use strict';
function appCtrl(){
var vm = this;
vm.appVar = "Hi from appCtrl";
}
angular.module('MyApp.controllers.appCtrl', [])
.controller('appCtrl', appCtrl);
})();
Done like that, and angular should have no troubles finding your controllers.

Dependency Injection with Require JS + Angular JS. App requires auth, but auth requires app

I'm trying to load an authService into my application using AngularJS + RequireJS. The app, requires authService to load, and authService requires app to load. But I can't seem to get the load to work properly.
I've tried setting a main controller for the application that will use the authProvider, but if I do that I get the following error:
Error: [$injector:unpr] Unknown provider: authServiceProvider <- authService
If I try to inject the authService into the app, I get this error:
Error: [$injector:modulerr] Failed to instantiate module app due to:
[$injector:modulerr] Failed to instantiate module authService due to:
[$injector:nomod] Module 'authService' is not available! You either misspelled the module name or forgot to load it.
Both errors make sense to me, and I know why they happen. I just don't know if there's a way around it beyond including authService into app.js (which I would like to avoid)
Example code is below.
app.js
define(function (require) {
var angular = require('angular');
var ngRoute = require('angular-route');
var authService = require('authService');
var app = angular.module('app', ['ngRoute']);
app.init = function () {
console.log('init');
angular.bootstrap(document, ['app']);
};
app.config(['$routeProvider', function ($routeProvider) {
console.log('config');
}]);
app.run(function () {
console.log('run');
});
var appCtrl = app.controller('appCtrl', function (authService) {
});
return app;
})
authentication.js
require(['app'], function (app) {
return app.factory('authService', ['$http', function ($http) {
return {
test: function () {
return this;
}
}
}]);
});
config.js
require.config({
baseUrl:'app',
paths: {
'angular': '../bower_components/angular/angular',
'angular-route': '../bower_components/angular-route/angular-route',
'angularAMD': '../bower_components/angularAMD/angularAMD',
'ngDialog': '../bower_components/ngDialog/js/ngDialog',
'ngCookies': '../bower_components/angular-cookies/angular-cookies',
'authService': 'services/authentication'
},
shim: {
'angular': {
'exports': 'angular'
},
'angular-route': ['angular'],
'angularAMD': ['angular'],
'ngCookies': ['angular']
},
priority: [
'angular'
],
deps: [
'app'
]
});
require(['app'], function (app) {
app.init();
});
index.html
<!DOCTYPE html>
<html lang="en" ng-controller="appCtrl">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div ng-view></div>
<script src="bower_components/requirejs/require.js" data-main="app/config.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.js"></script>
</body>
</html>
You described a circular reference per "The app, requires authService to load, and authService requires app to load" and there is simply no solution for it.
However, looking at the sample code you provided, your true dependency is:
authService needs to be created and made available for app
appCtrl created in app requires authService
Assuming that's your only dependency, you can use angularAMD to create authService:
require(['angularAMD'], function (angularAMD) {
angularAMD.factory('authService', ['$http', function ($http) {
return {
test: function () {
return this;
}
}
}]);
});
And make sure to use angularAMD to bootstrap app:
define(['angularAMD', 'angular-route', 'authentication'], function (require) {
var app = angular.module('app', ['ngRoute']);
...
return return angularAMD.bootstrap(app);
}]);
Take a look at Loading Application Wide Module for more details.

Combining angular with require

I went through this tutorial. Now I am attempting incorporate require
I found this explanation.
I am currently getting an error
Object #<Object> has no method 'unshift'
Here is the code that is causing the error
require(['jquery', 'angular', 'app/routes/app'], function ($, angular, mainRoutes) {
//tried this way as well
//$(function () { // using jQuery because it will run this even if DOM load already happened
// angular.bootstrap(document, ['mainApp']);
//});
require(['Scripts/app/modules/mainApp.js'], function (mainApp) {
angular.bootstrap(document.body, [mainApp]);//based of orginal answer
})
});
my app.js file
define(['app/modules/mainApp', 'app/controller/controllers'], function (mainApp) {
return mainApp.config(['$routeProvider', function ($routeProvider) {
$routeProvider.
when('/phones', {
templateUrl: 'Templates/phone-list.html',
controller: 'PhoneListCtrl'
}).
when('/phones/:phoneId', {
templateUrl: 'Templates/phone-detail.html',
controller: 'PhoneDetailCtrl'
}).
otherwise({
redirectTo: '/phones'
});
}]);
});
and my mainApp.js file
define(['angular', 'angular-resource'], function (angular) {
return angular.module('mainApp', ['ngResource']);
});
there are other files that I didnt show (controllers, services) but I dont think the problem lies their
UPDATE
I am now getting an error of undefined injector.
This is the only break point that gets hit, but the item is not undefined.
UPDATE 2
I updated my project to more resemble this
my main.js now is this
require.config({
baseUrl: '/Scripts/',
urlArgs: "bust=" + (new Date()).getTime(),
paths: {
'jquery': 'lib/require-jquery',
'angular': 'lib/angular/angular.min',
'angular-resource': 'lib/angular/angular-resource.min',
},
shim: {
'angular': { 'exports': 'angular' },
'angular-resource': { deps: ['angular'] },
'jQuery': { 'exports': 'jQuery' },
},
priority: [
'angular'
]
});
require(['angular', 'app/modules/app', 'app/routes/routes'], function (angular, app, routes) {
var $html = angular.element(document.getElementsByTagName('html')[0]);
angular.element().ready(function () { //breakpoint here
$html.addClass('ng-app');
angular.bootstrap($html, [app.name]);
});
});
if i put a break point on angular element and run a test in console
(app == routes)
true
should app be equal to routes?
The second argument of bootstrap method should be an array, I made the change on the code below.
require(['jquery', 'angular', 'app/routes/app'], function ($, angular, mainRoutes) {
//tried this way as well
//$(function () { // using jQuery because it will run this even if DOM load already happened
// angular.bootstrap(document, ['mainApp']);
//});
require(['Scripts/app/modules/mainApp.js'], function (mainApp) {
angular.bootstrap(document.body, [mainApp]);
})
});

Resources