I have an app that which has a load of scripts loading initially and the list is growing as development goes on. I want to lazy load scripts which contain controllers as and when needed. I am using OcLazyLoad along with ngRoute for the routing option (i did try ui-route which actually had the same end result but was causing other app issues). The lazy load and routing is working fine, scripts and views are only loaded when needed, but the issue is the controller is not being loaded (Argument 'caseReviewController' is not) so it's as though the controller does not exist.
Here is a simple version of what I have in my app.js file...
var app = angular.module('dashboard', ['oc.lazyLoad', 'ngRoute', 'LocalStorageModule']);
app.config(function ($ocLazyLoadProvider, $routeProvider, localStorageServiceProvider) {
$ocLazyLoadProvider.config({
loadedModules: ['dashboard'], modules: [
{
name: 'caseReview',
files: ['js/controllers/case-review.js']
}
]
});
$routeProvider
//other routes here...
.when('/case-review', {
templateUrl: 'views/case-review.html',
controller: 'caseReviewController',
resolve: {
loadModule: ['$ocLazyLoad', function ($ocLazyLoad) {
return $ocLazyLoad.load('caseReview');
}]
}
})
});
In the seperate case-review.js file I have a simple controller
app.controller('caseReviewController', function($scope, localStorageService, $route){
//do stuff
});
This controller is not being found or executed but the view and js file are being lazy loaded as expected. Any help would be great.
Thanks.
In your separate case-review.js, you must get the reference of app.
angular.module('dashboard').controller('caseReviewController', function($scope, localStorageService, $route){
//do stuff
});
As you've mentioned it's in separate file, it may not know about the app variable. It's better to get the reference of the angular module in the separate file.
This must solve your issue.
Related
I am using requireJS in my application.
Whenever i tried to register controller on my module it said that the controller is not defined. Here is my controller which resides on login.controller.js
function LoginController() {
}
and here's my module code:
require('angular')
require('#uirouter/angularjs');
require('./service/storage')
require('./controller/login.controller')
angular.module('SecurityModule', ['ui.router'])
.controller('LoginController', LoginController);
// Routing
angular.module('SecurityModule')
.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
$locationProvider.hashPrefix('');
$stateProvider.state('login', {
url: '/login',
templateUrl: '/app/resources/view/security/login.html',
controller: 'LoginController',
});
})
;
When i checked my bundled.js the declaration of LoginController appears first. So why is it still undefine?
Thanks.
NOTE that im using browserify (which then uses commonJS) to bundle my files.
As the documentation states:
A module is a collection of configuration and run blocks which get
applied to the application during the bootstrap process. In its
simplest form the module consist of collection of two kinds of blocks:
Configuration blocks - get executed during the provider registrations
and configuration phase. Only providers and constants can be injected
into configuration blocks. This is to prevent accidental instantiation
of services before they have been fully configured.
angular.module('myModule', []).
config(function(injectables) { // provider-injector
// This is an example of config block.
// You can have as many of these as you want.
// You can only inject Providers (not instances)
// into config blocks.
}).
run(function(injectables) { // instance-injector
// This is an example of a run block.
// You can have as many of these as you want.
// You can only inject instances (not Providers)
// into run blocks
});
I'm trying to get lazy-loaded Angular modules working with Webpack, but I'm having some difficulties. Webpack appears to generate the split point correctly, because I see a 1.bundle.js getting created that contains the code for the child app, but I don't see any request for 1.bundle.js when I load the page, and the child app doesn't initialize. The console.log never seems to fire, and it doesn't even appear to get to the point where $oclazyload would initialize the module.
There are a few points where I am confused.
1) Will webpack make the request to the server, or do I have to load the second bundle manually? (I've tried both, and neither works)
2) If I do need to load the bundles manually, in what order should they be loaded?
3) The third argument to require.ensure supposedly lets you control the name of the bundle, but the bundle is named 1.bundle.js no matter what string I pass.
4) Why can't I step through the code inside the require.ensure block in the debugger? When I do so I end up looking at this in the Chrome source view:
undefined
/** WEBPACK FOOTER **
**
**/
(Code Below)
Main entry point code:
'use strict';
import angular from 'angular';
import 'angular-ui-router';
import 'oclazyload';
angular.module('parentApp', [
'ui.router',
])
.config(['$urlRouterProvider', '$locationProvider', ($urlRouterProvider, $locationProvider) => {
$urlRouterProvider
.otherwise('/');
$locationProvider.html5Mode(true);
}])
.config(['$stateProvider', ($stateProvider) => {
$stateProvider
.state('child-app', {
url: '/child-app',
template: '<child-app></child-app>',
resolve: {
loadAppModule: ($q, $ocLazyLoad) => {
return $q((resolve) => {
require.ensure(['./child-app/app.js'], (require) => {
let module = require('./child-app/app.js');
console.log(module);
$oclazyload.load({name: 'childApp'});
resolve(module.controller);
});
})
}
},
controller: function() {
}
})
}]);
Child app:
'use strict';
import childAppTemplateURL from '../templates/child-app.html';
import childAppController from './controllers/childAppController';
export default angular.module('parentApp.childApp', [])
.component('applicationListApp', {
templateUrl: childAppTemplateURL,
controller: childAppController
});
The problem was unrelated to the require.ensure implementation. It was caused by some weirdness in the way ocLazyLoad is packaged (https://github.com/ocombe/ocLazyLoad/issues/179). The fix in my case was simple, I just added 'oc.lazyLoad' to the module dependencies.
angular.module('parentApp', [
'ui.router',
'oc.lazyLoad'
])
To answer two of my own questions, Webpack does indeed make a request to the server for the bundle, and you do not have to manually load the bundle. One gotcha that really confused me: the resolve block will fail silently if it contains a promise that won't resolve. In my case $ocLazyLoad.load() was failing, but there was no indication of the failure. The only clue was that the state provider wasn't adding the <child-app></child-app> markup to the DOM, which meant that it was actually initializing the state.
I am using a web templates which is use jquery image gallery or other etc.
I want to convert this template to angularjs structure. I make some partial html pages like slider.html or contact.html and its controller.
when i call partial html to index ,slider can not able to use jquery libraries which ı loaded them in index.
İf i refresh the page when partial is open it works again.
I read some article about directives or other etc. but ı am not writing any script they are just loaded in index and html uses them so i do not need any directive to work a script i just need use all library file in partial html.
is there any way ?
i try to load all libraries which partial will use but in this way all libraries loaded each call and it works but it is not a good way
I have some tips: First of all, your libraries should not be included in the view. You should include the library just in one point of your application. Could be asynchronous (via JavaScript), or by adding the script tag for the library in your index file.
If you decide add the library via JavaScript, take into account to do a check to prevent multiples includes, e.g.,
if (!window.jQuery) {
//include jQuery library
}
Also you can add the script tag in your index/main html file and if you want to load it asynchronously, you can add the attributes async or defer.
-- Take a look at HTML 5 <script> Tag.
Now, related to AngularJS, when you load a partial view, is raised the event $includeContentLoaded which is emitted every time the ngInclude content is reloaded.
If you have two or more partial views included, you should ask for the specific view where you want to load a library/plugin or make some DOM manipulation, e.g.,
controller.js
angular
.module('myApp')
.controller('HomeCtrl', [
'$scope', '$window',
function ($scope, $window) {
'use strict';
var homeCtrl = this,
$ = jQuery; //safe alias
//Emitted every time the ngInclude content is reloaded.
$scope.$on('$includeContentLoaded', function (event, source) {
if ('src/pages/home/zipcodeForm/view.html' === source) {
//initialize your jQuery plugins, or manipulate the DOM for this view
}
else if('src/pages/home/section1/view.html' === source){
//initialize your jQuery plugins, or manipulate the DOM for this view
}
});
//Can be used to clean up DOM bindings before an element is removed
//from the DOM, in order to prevent memory leaks
$scope.$on('$destroy', function onDestroy() {
//destroy event handlers
});
}
]);
The code that matters here is the event $includeContentLoaded. Visit the site for more information: https://docs.angularjs.org/api/ng/directive/ngInclude
If you are using ng-view you should have no problems, just register the view in the router (there are a lot of ways to achieve the same)
module.js
angular
.module('myApp', ['ngRoute' /*, other dependecies*/]);
router.js
angular
.module('myApp')
.config(['$routeProvider', '$locationProvider',
function ($routeProvider, $locationProvider) {
'use strict';
$routeProvider
.when('/', {
templateUrl: 'src/pages/home/view.html',
controller: 'HomeCtrl',
controllerAs: 'homeCtrl',
reloadOnSearch: false
})
.when('/index.html', {
redirectTo: '/'
})
.when('/home', {
redirectTo: '/'
})
.when('/404', {
templateUrl: 'src/pages/error404/view.html',
controller: 'Error404Ctrl',
controllerAs: 'error404Ctrl'
})
.otherwise({
redirectTo: '/404'
});
/*
Set up the location to use regular paths instead of hashbangs,
to take advantage of the history API
$locationProvider.html5Mode(true);
*/
}
]);
When the view is loaded, it emits the event: $viewContentLoaded meaning the DOM is loaded, then you can initialize jQuery plugins safely.
controller.js
angular
.module('myApp')
.controller('HomeCtrl', [
'$scope', '$window',
function HomeCtrl ($scope, $window) {
'use strict';
var homeCtrl = this, //the controller
$ = jQuery; //safe alias to jQuery
//Emitted every time the ngView content is reloaded.
$scope.$on('$viewContentLoaded', function() {
$('.slickSlider').slick({
slidesToShow: 4,
arrows: true,
slidesToScroll: 1
});
});
}
]);
Important: In the previous code, the order where scripts are included does matter.
module.js
router.js
controller.js
I highly recommend using automation tools such as gulp
I have a globale routes controller and want to create an additional controller for each page template.
It's done as follows:
var app = angular.module('myapp', ['ngSanitize', 'ngRoute']);
app.config(function($routeProvider){
$routeProvider
.when("/test", {
templateUrl: "./templates/test.html",
controller: "testController"
})
});
app.controller('test', function() {
//...
});
Question: how can I move the controller to it's own testController.js file? I tried it, but then the controller does not work anymore.
Which steps do I have to take if I extract the controller in its own file?
How can I get access to var app module variable from within testController.js?
Do I necessairly have to include each controller as new <script src="js/testController.js"></script> tag entry in my html templates? That would be cumbersome, as I want to split my application into many controllers, and that would result in many many imports.
You can access app by simply declaring it without dependencies:
var app = angular.module('myapp');
app.controller("testController", function() {
});
And yes, you need to include testController.js on every page that needs it.
I'm starting with angular, how could I break alll the code from one app into many files?, I watched the 60ish minutes intro, and they mentioned that I could do this without requirejs or any other framework.
Lets say I have something like this that works just fine:
var app = angular.module('app', []);
app.factory('ExampleFactory', function () {
var factory = {};
factory.something = function(){
/*some code*/
}
return factory;
});
app.controller ('ExampleCtrl', function($scope, ExampleFactory){
$scope.something = function(){
ExampleFactory.something();
};
});
app.config(function ($routeProvider) {
$routeProvider
.when('/',
{
controller: 'ExampleCtrl',
templateUrl: 'views/ExampleView.html'
})
.otherwise({ redirectTo: '/' });
});
What if I wanted to have it in separate files? like this
File One:
angular.module('factoryOne', [])
.factory('ExampleFactory', function () {
var factory = {};
factory.something = function(){
/*some code*/
}
return factory;
});
File Two:
angular.module('controllerOne', ['factoryOne'])
.controller ('ExampleCtrl', function($scope,ExampleFactory){
$scope.something = function(){
ExampleFactory.something();
};
});
File Three:
angular.module('routes', ['controllerOne'])
.config(function ($routeProvider) {
$routeProvider
.when('/',
{
controller: 'ExampleCtrl',
templateUrl: 'views/ExampleView.html'
})
.otherwise({ redirectTo: '/' });
});
File four:
var app = angular.module('app', ['routes']);
I've tried it like this and it doesn't work.
Can I do something like this and just have a script tag for File Four in the main view? or do I have to have one script tag per file?.
Thanks for the help guys.
AngularJS does not currently have a script loader as part of the framework. In order to load dependencies, you will need to use a third party loader such as RequireJS, script.js, etc.
Per the Docs(http://docs.angularjs.org/guide/module#asynchronousloading):
Asynchronous Loading
Modules are a way of managing $injector
configuration, and have nothing to do with loading of scripts into a
VM. There are existing projects which deal with script loading, which
may be used with Angular. Because modules do nothing at load time they
can be loaded into the VM in any order and thus script loaders can
take advantage of this property and parallelize the loading process.
...or, as #xanadont explained, you can add <script> tags to your page for every file.
You must have a
<script src="file.js"></script>
per each file that you're using. It should work once you have all the references in place.
Or ... check out this article for a way to roll-your-own runtime resolution of controllers.
You've got to separate the idea of downloading from the idea of loading and executing in-memory. Use Yeoman/grunt or similar build tools to manage the process of adding individual files to the project for Angular's various modules controllers, directives, services, etc. that are attached to those modules. Then, at build-time, the files will be minified and concatenated for a speed/bandwidth improvement that's vastly superior to lazy-downloading of individual files.
Once you've dealt with the files, Angular handles the rest, executing dependencies only when they're actually needed.
In the example above, #Jose, your problem was that you're not attaching your dependencies properly to the original module. You're creating new modules and burying the dependencies inside of them. In the first version, you used var app to cache the reference to the module called 'app' and then did app.controller(), etc. So, you're calling the .controller() method on the app module.
But in the second, you still need to attach those dependencies to the main app module. To do that, you need to call angular.module('app') to access the original module, then you can chain a call to .controller() or .directive() from that module, once you've retrieved it.
Bottom-line, use Angular's constructs for the loading of Angular dependencies. If, after you've gotten all of that out of the way, you still want to use Require for loading third-party scripts, go ahead. But I recommend testing first to see if you're actually adding value.