Implement RequireJs in AngularJs UI-Router - angularjs

I have application in angularJs and it will have different modules with different JS files.for js file optimization I am going to implement requireJS.
There is (broken) plunker
My angularJs code is like this in app.js:
var app = angular.module("webapp", ['ngRoute']);
app.run(['$rootScope', '$state','$urlRouterProvider',
function ($rootScope, $state,$urlRouterProvider) {
$urlRouterProvider.otherwise('/index.html');
$stateProvider
.state('root.home',{
url: '/index.html',
views: {
'header': {
templateUrl: 'modules/header/html/header.html',
controller: 'headerController'
},
'content-area': {
templateUrl: 'modules/home/html/home.html',
controller: 'homeController'
},
'footer': {
templateUrl: 'modules/common/html/footer.html',
controller: 'footerController'
}
},
data: {
displayName: 'Home',
}
})
.state('root.about',{
url: '/index.html',
views: {
'header': {
templateUrl: 'modules/header/html/header.html',
controller: 'headerController'
},
'content-area': {
templateUrl: 'modules/home/html/about.html',
controller: 'aboutController'
},
'footer': {
templateUrl: 'modules/common/html/footer.html',
controller: 'footerController'
}
},
data: {
displayName: 'About',
}
})
}]);
I added the following code in my main.js file
require.config({
baseUrl: "",
// alias libraries paths. Must set 'angular'
paths: {
'angular': 'http://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min',
'angular-route': 'http://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular-route.min',
'angularAMD': 'http://cdn.jsdelivr.net/angular.amd/0.2.0/angularAMD.min'
},
// Add angular modules that does not support AMD out of the box, put it in a shim
shim: {
'angularAMD': ['angular'],
'angular-route': ['angular']
},
// kick start application
deps: ['app']
});
and also added requirejs in html
<head>
<link rel="stylesheet" href="style.css">
<script data-main="main.js" src="http://marcoslin.github.io/angularAMD/js/lib/requirejs/require.js"> </script>
<script src="js/app.js"></script>
</head>
how can I define requirejs module or implement with my angularjs UI-Rooter?
EXTEND:
In your Example You added the above code in app.js
define([], function() {
var app = angular.module('webapp');
return app;
})
I added above code to script.js.Also In my app.js file contain all the UI router things and I changed the main.js with following code
require.config({
//baseUrl: "js/scripts",
baseUrl: "",
// alias libraries paths
paths: {
// here we define path to NAMES
// to make controllers and their lazy-file-names independent
"testController" : "modules/test/js/controller/testController",
},
deps: ['js/script'] // changed section
});
but in my browser console I am gettnig this error "NetworkError: 404 Not Found .../default/app.js" .how can I solve this issue.I directly added this js file through the html.but I am getting this error.

As discussed in comments and related to this plunker: http://plnkr.co/edit/iV7fuG5mkoTQ2JoKk9e0?p=preview
I followed the Q & A:
angular-ui-router with requirejs, lazy loading of controller
And updated that plunker and make it working.
There are many changes. E.g. we should keep a reference to controller providers:
var app_cached_providers = {};
app.config(['$controllerProvider',
function(controllerProvider) {
app_cached_providers.$controllerProvider = controllerProvider;
}
]);
And inside of our mapped controllers (e.g. controller_home.js) register that:
define(['app'], function (app) {
// the Content Controller
// is added into the 'app' module
// lazily, and only once
app_cached_providers
.$controllerProvider
.register('HomeCtrl', function ($scope) {
$scope.message = "Message from HomeCtrl";
});
});
Also, this would be a helper method to make other stuff a bit simplier
var loadController = function(controllerName) {
return ["$q", function($q) {
var deferred = $q.defer();
require([controllerName], function() {deferred.resolve(); });
return deferred.promise;
}];
}
And here we will use it to extend state definitions. Firstly the root state:
$urlRouterProvider.otherwise('/root');
$stateProvider
.state('root',{
url: '/root',
templateUrl: 'view_root.html'
});
Now states loading controller async way:
var root_home = {
//url: '/index.html',
url: '/home',
views: {
'' : {templateUrl: 'view_home.html', controller: 'HomeCtrl' },
},
data: {
displayName: 'Home',
},
resolve : { }
};
root_home.resolve.loadTopMenuCtrl = loadController("HomeCtrl");
var root_about = {
//url: '/about.html',
url: '/about',
views: {
'' : {templateUrl: 'view_view1.html', controller: 'View1Ctrl' },
},
data: {
displayName: 'About',
},
resolve : { }
};
root_about.resolve.loadContentCtrl = loadController("View1Ctrl");
$stateProvider
.state('root.home', root_home)
.state('root.about', root_about)
Check it all in action here

Related

Angular ui-route base controller extend

I'm really lost. I use ui-route in my angular app and i'm trying to extend my base controller. My base controller (appCtrl) works but child controller (navigationCtrl) doesn't on URL app/welcome. Do you know why???
index.html
<body>
<div id="wrap">
<!-- View for login and login-choose -->
<div ui-view="login"></div>
<!-- View for app -->
<div ui-view="app"></div>
</div>
<!-- SCRIPTS -->
<script type="text/javascript" src="./js/jquery-1.11.0.min.js"></script>
<script type="text/javascript" src="./js/jasny-bootstrap.min.js"></script>
<script type="text/javascript" src="./js/angular.min.js"></script>
<script type="text/javascript" src="./js/angular-ui-router.min.js"></script>
<script type="text/javascript" src="./js/angular-touch.min.js"></script>
<script type="text/javascript" src="./js/app.js"></script>
<script type="text/javascript" src="./js/app.navigation.js"></script>
</body>
app.js
var app = angular.module('tvm', ['ui.router', 'ngTouch']);
app.config(function($stateProvider, $urlRouterProvider, $locationProvider) {
// For any unmatched url, redirect to /state1
$urlRouterProvider.otherwise("/");
// Set up the states
$stateProvider
.state('login', {
url: "/",
views: {
"login": { templateUrl: "./pages/login.html" }
}
})
.state('login-choose', {
url: "/login-choose",
views: {
"login": { templateUrl: "./pages/login-choose.html" }
}
})
.state('app', {
url: "/app",
views: {
"app": {
templateUrl: "app.html",
controller: 'appCtrl'
}
}
})
.state('app.welcome', {
url: "/welcome",
templateUrl: './pages/welcome.html'
})
.state('app.profile', {
url: "/profile",
templateUrl: './pages/profile.html'
});
// remove # from URL
$locationProvider.html5Mode({
enabled: true,
requireBase: false
});
});
app.controller('appCtrl', function($scope) {
$scope.parentMethod = function () {
alert('aaa');
};
});
app.navigation.js
app.controller('navigationCtrl', ['$scope', '$controller', function($scope, $controller) {
// extend app controller
angular.extend(this, $controller('appCtrl', {$scope: $scope}));
var nav = $('nav');
var content = $('#content, #bar');
var navWidth = nav.width() + 'px';
var navIcon = $('#bar .nav_icon .circle');
$scope.circleHover = function(e) {
navIcon.addClass('hover');
};
$scope.circleLeave = function(e) {
navIcon.removeClass('hover');
};
// open / close nav
$scope.toggleNav = function(e) {
if(nav.hasClass('visible'))
closeNav();
else
openNav();
};
// when swipe left to right open nav
$scope.openSwipeNav = function(e) {
openNav();
};
// close nav
$scope.closeNav = function(e) {
var elem = angular.element(e.target);
if( (nav.hasClass('visible')) && (!$(elem).is('#bar')) )
closeNav();
};
// when swipe right to left close nav
$scope.closeSwipeNav = function(e) {
closeNav();
};
var openNav = function() {
nav.animate({
left: 0
}, 400, function() {
$(this).addClass('visible');
});
content.animate({
left: navWidth
}, 400, function() {
$(this).removeClass('full');
});
navIcon.addClass('active');
};
var closeNav = function() {
nav.animate({
left: '-' + navWidth
}, 400, function() {
$(this).removeClass('visible');
});
content.animate({
left: 0
}, 400, function() {
$(this).addClass('full');
navIcon.removeClass('active');
navIcon.removeClass('hover');
});
};
}]);
You are providing the states in wrong way should use the below code. It may help you get out of your problem
// Set up the states
$stateProvider
.state('login', {
url: "/",
views: {
"login": { templateUrl: "./pages/login.html" }
}
})
.state('login-choose', {
url: "/login-choose",
views: {
"login": { templateUrl: "./pages/login-choose.html" }
}
})
.state('app', {
url: "/app",
templateUrl: "app.html" //no need to define the controller here. If this route is not usable else define the controller separately for both app and app.welcome
})
.state('app.welcome', {
url: "/welcome",
templateUrl: './pages/welcome.html',
controller: 'appCtrl' // need to mention the controller in which you wantto perform the functionality of app/welcome
})
.state('app.profile', {
url: "/profile",
templateUrl: './pages/profile.html'
});
Ok I managed to solve it by add angular.extend to base controller
app.controller('appCtrl', ['$scope', '$controller', function($scope, $controller) {
angular.extend(this, $controller('navigationCtrl', {$scope: $scope}));
}]);
And I call controller in my first state:
.state('app', {
url: "/app",
views: {
"app": {
templateUrl: "app.html",
controller: 'appCtrl'
}
}
})
Then it also fires navigationCtrl

app.config + stateProvider

I am using ui.router to handle my app routing, currently my app is small online a couple routes. As of right now they go into app.config, i'd like to spilt out each route configuration based on feature, for example this feature is a for a "Promotion" SPA, how can I go about doing this so I won't clutter my initial app.config js file?
$stateProvider
.state('promotion', {
controller: 'PromotionsController',
url: '',
views: {
"list": {
controller: 'PromotionsController',
templateUrl: templatesRoot + 'Promotion/promotion-list.html'
},
"editor": { template: "Welcome" }
}
})
.state('promotion-edit',
{
url: '/edit/{id}',
views: {
"list": {
controller: 'PromotionsController',
templateUrl: templatesRoot + 'Promotion/promotion-list.html'
},
"editor": {
controller: ['$scope', '$stateParams', 'promotionService', function ($scope, $stateParams, promotionService) {
$scope.promotion = promotionService.getPromotion($stateParams.id)
$scope.savePromotion = function () {
// save the promotion
promotionService.savePromotion($scope.promotion, function (data, responseHeaders) {
if (!data.Success) {
toaster.pop({
type: 'error',
title: 'Error',
body: data.Message,
showCloseButton: true
});
} else {
toaster.pop({
type: 'success',
title: "Success",
body: "Successfully saved the promotion",
showCloseButton: true
});
}
});
};
}],
templateUrl: templatesRoot + 'Promotion/promotion-edit.html'
}
}
}
)
.state('promotion-create',
{
url: '/create',
views: {
"list": {
controller: 'PromotionsController',
templateUrl: templatesRoot + 'Promotion/promotion-list.html'
},
"editor": {
controller: 'PromotionsController',
templateUrl: templatesRoot + 'Promotion/promotion-create.html'
}
}
}
)
Separate your routes into files. For example:
promotion.js
angular.module('AppName').config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) {
$stateProvider.state('promotion', {
controller: 'PromotionsController',
url: '',
views: {
"list": {
controller: 'PromotionsController',
templateUrl: templatesRoot + 'Promotion/promotion-list.html'
},
"editor": { template: "Welcome" }
}
})
}]);
Then in your index.html, reference this file:
<script src="pathToRoutes/promotion.js"></script>
After that you should be good to go.
Just put it in a separate files which contain only config blocks.
For example you can have:
app/scripts/app.js # not router configuration
app/scripts/routes/promotions.js # pomotions configuration
app/scripts/routes/featureN.js # n-th feature
app/scripts/routes/custom-states-provider.js
The last one you will need if you notice a lot of code duplication between the features and you want to make a custom wrapper around the ui router $stateProvider with utility functions. Of course there are other solutions to this problem.

oclazyLoad with webpack to support lazy loading for Angularjs is not working on minification [duplicate]

When I uglify webpack bundle, routing stops work without any error message or log message. I am using oclazyload to lazy load.
Route.js
module.exports = function(app) {
var routeConfig = function($stateProvider) {
$stateProvider
.state('home', {
url: '/',
templateUrl: 'app/dashboard/dashboard.min.html',
title: 'Home',
ncyBreadcrumb: {
label: 'Home'
}
})
.state('organizationStructure', {
url: '/organizationStructure',
templateUrl: 'app/admin/organizationStructure/manageHierarchy/manageHierarchyShell.min.html',
'abstract': true,
ncyBreadcrumb: {
skip: true
},
resolve: ['$q', '$ocLazyLoad', function($q, $ocLazyLoad) {
var deferred = $q.defer();
require.ensure([], function() {
var mod = require('./organizationStructure.module.js');
$ocLazyLoad.load({
name: 'app.organizationStructure'
});
deferred.resolve(mod.controller);
});
return deferred.promise;
}]
})
.state('organizationStructure.organization', {
url: '/organization',
templateUrl: 'app/admin/organizationStructure/manageHierarchy/organization/index.min.html',
controller: 'ManageOrganization',
controllerAs: 'vm',
title: 'Manage Organization',
ncyBreadcrumb: {
label: 'Manage Organization',
parent: 'home'
}
});
}
app.config(routeConfig);
return routeConfig;
};
Module.js
var app = angular.module('app', [
'ui.router',
'restangular',
'ui.bootstrap',
'ncy-angular-breadcrumb',
'oc.lazyLoad'
]);
Base Route
require('./app.route.js')(app);
When I minify the bundle, app routing stops working. Otherwise it works fine. Please provide me a solution. Also I am using ngAnnotate. Dependencies are injected safely in minified file.
While doing minification you should for array annotation of DI.
You are not using angular di array notation inside you app.js, you need to do below changes.
From
app.config(routeConfig);
To
app.config(['$stateProvider', routeConfig]);
For more Information Refer this SO Answer
You're using the latest version of ui-router, which has a newer and slightly different way of handling the resolve block. It takes an object map.
Try this:
resolve: {
foo: ['$q', '$ocLazyLoad', function($q, $ocLazyLoad) {
var deferred = $q.defer();
require.ensure([], function() {
var mod = require('./organizationStructure.module.js');
$ocLazyLoad.load({
name: 'app.organizationStructure'
});
deferred.resolve(mod.controller);
});
return deferred.promise;
}]
}
Here is the ui-router API doc for this: https://github.com/angular-ui/ui-router/wiki#resolve

Using AngularJS with RequireJS via AngularAMD: Cannot read property 'directive' of undefined

Having created a very basic prototype AngularJS project, I wanted to migrate it to use RequireJS to load the modules. I modified my app based on the AngularAMD and AngularAMD-sample projects.
Now, when I access my default route I get:
Uncaught TypeError: Cannot read property 'directive' of undefined
I've been scratching my head as to why the dependency on 'app' is not being satisfied. If anyone can spot what I'm obviously doing wrong, it'd be much appreciated.
I've put the source code of my project here on GitHub, but here's the key parts:
main.js
require.config({
baseUrl: "js/",
// alias libraries paths
paths: {
'angular': '../bower_components/angular/angular',
'angular-route': '../bower_components/angular-route/angular-route',
'angular-resource': '../bower_components/angular-resource/angular-resource',
'angularAMD': '../bower_components/angularAMD/angularAMD',
'ngload': '../bower_components/angularAMD/ngload',
'jquery': '../bower_components/jquery/jquery'
},
// Add angular modules that does not support AMD out of the box, put it in a shim
shim: {
'angularAMD': ['angular'],
'ngload': [ 'angularAMD' ],
'angular-route': ['angular'],
'angular-resource': ['angular']
},
// kick start application
deps: ['app']
});
app.js
define(['angularAMD', 'angular-route', 'controller/login', 'controller/project_detail', 'controller/project_list'], function (angularAMD) {
'use strict';
var app = angular.module('cmsApp', ['ngRoute']);
app.constant('REMOTE_BASE_URL', "/cms/v2/remote");
app.constant('SERVER_ERROR_TYPES', {
authentication: 'Authentication',
application: 'Application',
transport: 'Transport'
});
app.constant('AUTH_ERROR_TYPES', {
invalidLogin: "INVALID_CREDENTIALS",
invalidToken: "INVALID_TOKEN",
noToken: "NO_TOKEN"
});
app.constant('AUTH_EVENTS', {
loginSuccess: 'auth-login-success',
loginFailed: 'auth-login-failed',
logoutSuccess: 'auth-logout-success',
notAuthenticated: 'auth-not-authenticated'
});
app.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/login', {
templateUrl: 'partials/login.html',
controller: 'LoginCtrl'
}).
when('/projects', {
templateUrl: 'partials/project-list.html',
controller: 'ProjectListCtrl'
}).
when('/projects/:projectId', {
templateUrl: 'partials/project-detail.html',
controller: 'ProjectDetailCtrl'
}).
otherwise({
redirectTo: '/projects'
});
}]);
return angularAMD.bootstrap(app);
});
And the file which the exception is being raised in:
login_form.js
define(['app'], function (app) {
app.directive('loginForm', function (AUTH_EVENTS) {
return {
restrict: 'A',
template: '<div ng-if="visible" ng-include="\'partials/login.html\'">',
link: function (scope) {
scope.visible = false;
scope.$on(AUTH_EVENTS.notAuthenticated, function () {
scope.visible = true;
});
scope.$on(AUTH_EVENTS.loginFailed, function () {
alert("An error occured while trying to login. Please try again.")
scope.visible = true;
});
scope.$on(AUTH_EVENTS.logoutSuccess, function () {
scope.visible = true;
});
}
};
});
});
You are loading 'controller/login' before the app itself was created.
Probably it is better to create a separate module like
define(['directive/login_form', 'service/authentication'], function () {
'use strict';
var loginModule = angular.module('loginModule', []);
loginModule.controller('LoginCtrl', ...
loginModule.directive('loginForm', ...
and then do something like
var app = angular.module('cmsApp', ['ngRoute', 'loginModule']);
Does that make sense?
UPDATE:
I am just thinking of another solution. Just remove 'controller/login' from your app define. Using angularAMD your controller should not be loaded anyway before you navigate to the specified url. Just remove it and your controller gets loaded on demand. That way, app will be defined! (Although I would still suggest to create multiple modules. It feels better to not have everything in the app module but have multiple modules for different responsibilities. Also much better for testing.)
angularAMD.route({
templateUrl: 'views/home.html',
controller: 'HomeController',
controllerUrl: 'scripts/controller'
})
Note the field controllerUrl.
Have a look here.

how to reference a controller inside a sub-module in angularjs

I'm using modules /sub modules on the angular app, my controller doesn't load on a specific route but the view does, according to a comment on this question I should reference the child module inside the main module and that should do the trick.
this is my code for bootstrapping the app:
angular.module('mainApp', ['ui.bootstrap', 'ui.utils', 'ui.router', 'ngResource', 'ngAnimate', 'ngCookies', 'facebook', 'subModule1', 'subModule2', 'subModule3']);
angular.module('mainApp').config(function ($stateProvider, $urlRouterProvider, $locationProvider, FacebookProvider) {
$stateProvider.state("root",
{
url: '',
abstract: true,
views: {
'footer#': {
templateUrl: "/partial/footer/footer.html",
},
'header#': {
templateUrl: "/partial/header/header.html",
}
}
}).state('root.home', {
url: '/index',
views: {
'container#': {
templateUrl: '/partial/index/index.html',
controller: 'IndexCtrl'
}
},
}
).state('root.login', {
url: "/login",
views: {
'container#': {
templateUrl: '/partial/login/login.html',
controller: 'LoginCtrl'
}
},
});
FacebookProvider.init('xxxxxx');
$urlRouterProvider.otherwise('/index');
$locationProvider.hashPrefix('!');
});
I have the sub-module configuration in a separate folder named /subModule1/submodule1.js
angular.module('subModule1').config(function($stateProvider) {
$stateProvider.state("submodule1",
{
url: '',
abstract: true,
views: {
'footer#': {
templateUrl: "/partial/footer/footer.html",
},
'header#': {
templateUrl: "/partial/header/header.html",
}
}
}).state('submodule1.dashboard',
{
url: '/dashboard',
views: {
'container#': {
templateUrl: '/subModule1/partial/dashboard/dashboard.html',
controller: 'DashboardCtrl',
resolve: {
dashboardinfo: function($resource) {
var resourceGet = $resource('/submodule1/dashboard');
return resourceGet.get().$promise;
}
}
},
'sideBar#': {
templateUrl: '/submodule1/partial/sidebar/sidebar.html'
},
'navBar#': {
templateUrl: '/submodule1/partial/navbar/navbar.html'
}
}
});
});
the controller is defined as:
angular.module('subModule1').controller('DashboardCtrl', function ($scope, $interval, $resource, notification, dashboardinfo) { ... }
the index located on the root of the page which is the page layout have the
<html ng-app="mainApp">
and the controller have the ng-controller definiton as follows:
<div ng-controller="DashboardCtrl">
Everything is fine just the controller isn't running, it doesn't get executed by the view.
The ui-router and ng-controller="DashboardCtrl" are intended to work together. In the ui-router world we are assigning Controllers to views directly in the state definition.
So this (exactly as you have already have it, no change) is enough:
.state('submodule1.dashboard',
{
url: '/dashboard',
views: {
'container#': {
templateUrl: '/subModule1/partial/dashboard/dashboard.html',
controller: 'DashboardCtrl',
to say, that the view rendered inside of the ui-view="container" on the root (index.html) should be provided with DashboardCtrl.
There is an example using the above state definition (1:1 as possible).
This is the index.html content:
<div ui-view="header"></div>
<div ui-view="navBar"></div>
<div ui-view="container"></div>
<div ui-view="sideBar"></div>
<div ui-view="footer"></div>
And this links will correctly trigger the above states:
// root
<li><a ui-sref="root.home">root.home</a></li>
<li><a ui-sref="root.login">root.login</a></li>
// dashboard
<li><a ui-sref="submodule1.dashboard">submodule1.dashboard</a></li>
All the other details check here

Resources