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.
Related
I have a project that looks like this:
website/
js/
modules/
axios.min.js
app.js
factory.js
app.js
For some reason, there are two app.js files (the project is not mine, I'm just working on it right now) and both of them look exactly the same. This project is a frontend for an application, and it's written on AngularJS.
I need to include axios in this project for some stuff, so I placed the axios.min.js file inside the modules folder (as I was told to do, because this is not a node project and we coudln't just npm install axios) and now I'm trying to import it to the correct place.
Here are some snippets of the files in question:
website/app.js and website/js/app.js:
var App = angular.module('App', ['ngResource', 'ui.router', "pascalprecht.translate", "ngSanitize", 'angular-loading-bar', 'oc.lazyLoad', 'smart-table', 'angular-growl', 'ngAnimate', 'ngMask', 'ngFileUpload'])
App.constant("URL_BASE", "http://localhost/php/");
App.constant("NUM_PAGE", "10");
App.constant("ESPANOL", true);
App.constant("INGLES", false);
App.constant("FRANCES", false);
//App.constant("URL_BASE", "https://activities.domain.com/php/");
//App.constant("URL_BASE", "localhost/php/");
App.run(function ($rootScope, ESPANOL, INGLES, FRANCES)
{
// stuff
});
website/js/factory.js:
App
.factory('Api', function () {
return {
getTracks: function () {
}
}
})
Now that this is all laid out, here's my problem:
I need to use axios inside the getTracks function. At first, I tried putting import {axios} from "./modules/axios"; at the start of the file, but that got me an error saying import declarations may only appear at top level of a module. So my next thought was to have axios inside a constant in the App, that the function would gain access through the factory function. So I moved the import declaration to website/app.js, like this:
var App = angular.module('App', ['ngResource', 'ui.router', "pascalprecht.translate", "ngSanitize", 'angular-loading-bar', 'oc.lazyLoad', 'smart-table', 'angular-growl', 'ngAnimate', 'ngMask', 'ngFileUpload'])
App.constant("URL_BASE", "http://localhost/php/");
App.constant("NUM_PAGE", "10");
App.constant("ESPANOL", true);
App.constant("INGLES", false);
App.constant("FRANCES", false);
import {axios} from "./js/modules/axios";
App.constant("axios", axios);
App.run(function ($rootScope, ESPANOL, INGLES, FRANCES)
{
// stuff
}
And that works, it imports axios correctly and stores it in that constant. However, when I modified the factory function like below to access axios, I got an Unkown Provider error:
.factory('Api', function (axios) {
return {
getTracks: function () {
// just to test
axios.get('google.com').then(response => {console.log(response)});
}
}
})
So then I started tinkering with the whole project changing small bits back and forth until I figured out that the constants in website/app.js mean nothing, and that the constants in website/js/app.js are the ones I'm getting in the factory function. So I just moved the import there, but that gets me the import declarations may only appear at top level of a module error again. I can't get rid of website/app.js, and I can't find a way to get axios to be a constant in the App declared in website/js/app.js so I'm completely lost here.
It's also worth mentioning that I've never worked with angular before, and that I have very little experience with front end development (I'm only working on this because I have to set up some API calls), and since this is an old, old project I've been assigned to work on, the documentation does not help a single bit and looks nothing like what I have here.
In conclusion, I need to use axios in website/js/factory.js, and I need to import it somewhere from website/js/modules/, using the import keyword, because require is apparently not a thing.
Edit: Also, better suggestions for the title of this post are welcome, I could not think of anything short but descriptive
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 start a new Angular 1 Application based on ES6. I use webpack and the babel-loader to convert the JS.
My problem now is to load an own config module. Please have a look at this:
// config/config.js
import angular from 'angular';
export default angular.module('config')
.factory('config', () => {
return {
url: {
products: 'https://....'
},
products: []
}
})
The corresponding app.js reads (I stripped some imports):
import angular from 'angular';
import config from './config/config';
import HomeCtrl from './controller/HomeController';
let app = () => {
return {
template: require('./app.html')
}
};
const MODULE_NAME = 'app';
angular.module(MODULE_NAME, [uiRouter, config])
.directive('myapp', app)
.config(['$stateProvider', '$urlRouterProvider', 'config', function ($stateProvider, $urlRouterProvider, config) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state("home", {
"url": "/",
"template": require('./views/home.html'),
"controller": HomeCtrl,
'controllerAs': 'app'
})
}]);
export default MODULE_NAME;
The error message says:
Uncaught Error: [$injector:nomod] Module 'config' 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.
What did I missed here? Is there a better way to load an application wide config object to use in certain services?
Thanks for help!
When you create a module, you have to call module() with two args: the name and the dependencies. If you call it with only one parameter, you get the existing module with that name.
Change your module config declaration to:
export default angular.module('config', [])
.factory('config', () => {
return {
url: {
products: 'https://....'
},
products: []
}
}).name
In addition, I always export only the .name of a new module. When you import the module, you just need its name.
Hope it helps.
When you declare your dependent modules in your main module definition you need to use the string identifier, not the actual angular.module (at least here it's specified as Array[String]: https://docs.angularjs.org/api/ng/function/angular.module
So, you should change to this:
angular.module(MODULE_NAME, ['config'])
Please check Angular documentation - module dependencies it's array of strings, but not dependent modules types or instances
angular.module(name, [requires], [configFn]);
when
requires (optional) Array<string>
So your solution to use next declaration
angular.module(MODULE_NAME, ['config'])
https://docs.angularjs.org/api/ng/function/angular.module
I am trying to lazy load an Angular directive as a webpack chunk.
Here is my current config attempt at using ocLazyLoad:
// Basic Config
function routingBase( $urlRouterProvider, $locationProvider, $stateProvider ) {
$locationProvider.html5Mode(true);
$stateProvider
.state('home', {
url: '/home',
template: '<app-main></app-main>',
resolve: {
load: ( $q, $ocLazyLoad ) => {
let deferred = $q.defer();
require.ensure([], (require) => {
// Load entire module
let module = require('../modules/main');
$ocLazyLoad.load({
name: module.name
});
deferred.resolve(module);
}, 'app-main');
return deferred.promise;
}
}
})
}
This goes in myModule.config(routingBase);.
../modules/main is just an angular module that exports a directive (e.g. export default angular.module('main',[]).directive('appMain', appMainFn);.
Any tips? What I am getting is that the <app-main></app-main> is correctly added to the document, and that the chunk is correctly loaded as module. But it is not replaced (it stays as <app-main></app-main>).
Would you recommend a different method for lazy loading chunks (maybe using $compileProvider)? I would like the cleanest possible way.
Thank you very much for your help.
I was able to get this working in my current project. Just change the following line and see it works. You need to add default as well since you are exporting angular module as default. Cheers!
$ocLazyLoad.load({
name: module.default.name
});
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.