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
Related
Updated: I did not use any module loader, because this is old project, emm, so I just import all dependencies in my index.html via script tag
My AngularJS has a structure like this:
app.js
angular.module('app', ['LocalStorageModule', 'ngCookies', ...])
testController
angular.module('app').controller('testController', function(){})
now I want to test testController, so my jest UT code:
testController.spec.js
require('./testController.controller')
describe('TestController', () => {
beforeEach(angular.mock.module('app'));
})
but now I got an error:
Module 'app' is not available
that means I must import app.js, but if I import app.js, I also got
Failed to instantiate module LocalStorageModule due to: Module 'LocalStorageModule' is not available!
So I have to import all my dependencies(twenty an more installed by bower) in my every test file? I think it isn't a good way. How to handle this solution? import all my components installed by bower?
The problem seems to be the angular.js dependency injection. Because you have some dependencies declared in your app module. You need to mock your app module and then you need to inject the dependencies.
describe('TestController', () => {
beforeEach(
angular.mock.module('app')
);
let _localStorageModule;
let _ngCookies;
beforeEach(
inject((LocalStorageModule, ngCookies) => {
_localStorageModule = LocalStorageModule;
_ngCookies = ngCookies;
})
);
})
EDIT: I haven't found a way to import templates with an import statement instead of require, but I have discovered that I can simplify my configuration.
In the webpack config, use html-loader instead of ngtemplate-loader for /\.html$/. Then, in the component, require the template at the top of the file like we did in the original post, but change "templateUrl" to "template" in the component definition. Webpack will do the rest for you.
const template = require('./component.html');
class Example(){
...
}
export const component = {
bindings: {},
controller: Example,
template: template
};
original post:
I have a working solution with ngtemplate-loader:
const template = require('./component.html');
import ExampleService from './example.service';
class Example() {
constructor(private exampleService: ExampleService) {
}
...
}
export const component = {
bindings: {},
controller: Example,
templateUrl: template
};
But I'm afraid my colleagues will object to the require syntax. You see, I am migrating our app from Grunt to Webpack2, and the current app only uses import foo from './bar' syntax.
We also don't import templates in the javascript files in our current build; we use a callback in the templateUrl to return a string.
import ExampleService from './example.service';
class Example() {
constructor(private exampleService: ExampleService) {
}
...
}
export const component = {
bindings: {},
controller: Example,
templateUrl: ['constantsFactory', function(constantsFactory) {
return `${constantsFactory.path}/component.html`;
}]
};
Is there a webpack loader that can populate $templateCache without require?
If not, is there a way to import templates in Typescript with import syntax?
There is not a way to add templates to the $templateCache by using import syntax, but you can add templates to the cache with 'require' syntax.
angular1-templateurl-loader
Best loader I could find right now.
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've using the angular fullstack generator for a long time and I recently updated it and noticed it no longer uses bower and that it uses require to get the modules in the client. My issue is that I can't understand how this works at all when I try to declare a service.
if I run, for example, yo angular-fullstack:service example it creates a example.service.js in the client/app/example path with the following code:
'use strict';
const angular = require('angular');
/*#ngInject*/
export function exampleService() {
// AngularJS will instantiate a singleton by calling "new" on this function
}
export default angular.module('sisaApp.example', [])
.service('example', exampleService)
.name;
Let's say that I want to use $http here. Do I pass it as a parameter to the function?
Apart from that, how do I go about injecting this service into a controller?
If I run yo angular-fullstack:route myroute and it generates th following controller file:
'use strict';
const angular = require('angular');
const uiRouter = require('angular-ui-router');
import routes from './myroute.routes';
export class MyrouteComponent {
/*#ngInject*/
constructor() {
this.message = 'Hello';
}
}
export default angular.module('sisaApp.myroute', [uiRouter])
.config(routes)
.component('myroute', {
template: require('./myroute.html'),
controller: MyrouteComponent,
controllerAs: 'myrouteCtrl'
})
.name;
Doing it the old was by passing it as a parameter into the constructor doesn't seem to be working for me.
Any help would be appreciated!
Thanks in advance!
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
});