AngularJS: how to use $http in config phase - javascript

I am building a single page application using angular. We use Continuous Integration with several Dev and QA environments. This means that every time I deploy the app in a different environment I need to update some config parameters such as SPA baseUrl, rest api url or url for my oauth server.
To achieve this, our CI engine generates a config.json file with the correct parameters for each environment when we are about to deploy there. To get this I have created a really simple service:
(function () {
'use strict';
angular
.module('app.core')
.factory('config', config);
config.$inject = ['$http'];
function config($http) {
return {
load: load
};
//////////
function load() {
return $http.get('assets/config/config.json');
}
}
}());
Most of my services are configured through providers during the config phase. And here is where the problem comes up. As you know, services cannot be injected during this phase but I would like to use config service to set up my providers with the correct URLs and parameters.
Is there a proper way to do this?
The only thing I can consider so far is having these config parameters embed in my module creation file so they are there ready to use. It seems quite ugly since I donĀ“t want any external process to my app modifying my source code.

If these are asynchronous services that rely on config.json, it is perfectly ok to change the way they are configured and fit your config into async workflow, e.g.
app.factory('config', function ($http) {
return $http.get('config.json', { cache: true })
.then(function (response) {
return response.data;
});
});
app.factory('asyncService', function ($http, config) {
return config.then(function (config) {
return $http...
}).then(...);
});
Otherwise the best option is to build json config to a service with Gulp/Grunt/whatever.
Think of
angular.module('config', []).constant('config', <%= configJson %>);
as of a compiled template rather than 'external process to my app modifying my source code'.

What I am doing at the moment (but I am open for a better solution) is to have a separate config file with the json object that contains all the config info.
Example of the file content:
var appConfigInfo = {
DEV: true,
API_PATH: 'your_custom_url'
// etc.
};
Then I add it as a constant in my app. Example:
angular
.module('app', [])
.constant('APP_CONFIG', appConfigInfo);
Now you can access all your configuration values via the constant. Don't forget to inject it in your controllers, etc.
I hope it helps.

Related

Getting Error: Unkown provider: $urlMatcherFactory

Unkown provider: $urlMatcherFactory
This is the error message that Angular throws when I try to inject $urlMatcherFactory in the app config.
I have included the ui-router JS file and have been using it for last few months.
I need to do something like this:
var dateMatcher = $urlMatcherFactory.compile("/day/{start:date}");
$stateProvider.state('calendar.day', {
url: dateMatcher
});
as this example shows.
Normally code like this won't identify $urlMatcherFactory. So I tried injecting it as a dependency on the config but then I get this error.
In this Q&A Matching url with array list of words in AngularJS ui-router you can see how to use the $urlMatcherFactory. Also a link to working plunker .
In that example, the $urlMatcherFactory is used in the .run():
.run(['$urlMatcherFactory',
function($urlMatcherFactory) {
var sourceList= ['John', 'Paul', 'George', 'Ringo']
var names = sourceList.join('|');
var urlMatcher = $urlMatcherFactory.compile("/{source:(?:" + names + ")}/:id");
$stateProviderRef
.state('names', {
url: urlMatcher,
templateUrl: 'tpl.root.html',
controller: 'MyCtrl'
});
}
]);
And that would also mean, that if you are about to use it in a config phase, you should ask for
$urlMatcherFactoryProvider (see the Provider at the end)
BUT: using providers in a config phase means - we can configure them. I mean configure the provider itself. To be later evaluable (already configured) in run phase
Configuring Providers
You may be wondering why anyone would bother to set up a full-fledged provider with the provide method if factory, value, etc. are so much easier. The answer is that providers allow a lot of configuration. We've already mentioned that when you create a service via the provider (or any of the shortcuts Angular gives you), you create a new provider that defines how that service is constructed. What I didn't mention is that these providers can be injected into config sections of your application so you can interact with them!
First, Angular runs your application in two-phases--the config and run phases. The config phase, as we've seen, is where you can set up any providers as necessary. This is also where directives, controllers, filters, and the like get set up. The run phase, as you might guess, is where Angular actually compiles your DOM and starts up your app.

AngularJS and Webpack Integration

I am looking for some help with using webpack for a large AngularJS application. We are using folder structure based on feature (each feature/page has a module and they have controllers, directives). I have successfully configured webpack to get it working with Grunt, which produces one single bundle. I want to create chunks as its going to be a large app, we would like to load modules (page/feature) artifacts asynchronously.
I am going through some of the webpack example to use 'code splitting' using require([deps],fn ) syntax. However I couldn't get the chunks lazy-loaded. First of all, I don't know where exactly, I would need to import these chunks before AngularJS would route the user to next page. I am struggling to find a clear separation of responsibility.
Did someone point me to an example AngularJS application where webpack is used to load controllers/directives/filters asynchronously after each route?
Few of the links I am following:
Should I use Browserify or Webpack for lazy loading of dependancies in angular 1.x
https://github.com/petehunt/webpack-howto#9-async-loading
http://dontkry.com/posts/code/single-page-modules-with-webpack.html
Sagar Ganatra wrote a helpful blog post about code splitting.
Suprisingly code splitting isn't really supported by angular's module system. However, there is a way to achieve code splitting by saving a reference to angular's special providers during the config-phase.
[...] when Angular initializes or bootstraps the application, functions - controller, service etc,. are available on the module instance. Here, we are lazy loading the components and the functions are not available at a later point; therefore we must use the various provider functions and register these components. The providers are available only in the config method and hence we will have to store a reference of these providers in the config function when the application is initialized.
window.app.config([
'$routeProvider',
'$controllerProvider',
'$compileProvider',
'$filterProvider',
'$provide',
function ($routeProvider, $controllerProvider, $compileProvider, $filterProvider, $provide) {
$routeProvider.when('/login', {
templateUrl: 'components/login/partials/login.html',
resolve: {
load: ['$q', '$rootScope', function ($q, $rootScope) {
var deferred = $q.defer();
// lazy load controllers, etc.
require ([
'components/login/controllers/loginController',
'components/login/services/loginService'
], function () {
$rootScope.$apply(function () {
deferred.resolve();
});
});
return deferred.promise;
}]
}
});
//store a reference to various provider functions
window.app.components = {
controller: $controllerProvider.register,
service: $provide.service
};
}
]);
Now inside your loginController for instance you write
app.components.controller('loginController');
to define your new controller lazily.
If you want to lazy-load your templates too I recommend to use the ui-router. There you can specify a templateProvider which is basically a function to load templates async
This is a quote from
https://github.com/webpack/webpack/issues/150
webpack is a module bundler not a javascript loader. It package files from local disk and don't load files from the web (except its own chunks).
Use a javascript loader, i. e. script.js
var $script = require("scriptjs");
$script("//ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular.min.js", function() {
// ...
});

Create a Reusable AngularJS provider/directive/factory, etc

I am attempting to create a number of AngularJS libraries that can be re-used across apps and modules. I thought i was doing everything correctly but am still having a problem.
First I create a file that defines a generic module (app.ui) and attaches a provider (LayoutManager) to it ... (I am using a jQuery plugin called "jQuery Layout". This provider allows the app to access and manipulate the layout parameters. I don't want to get hung up on the details of the plugin however. The question is more general, but thought I should at least provide some explanation)
angular.module("app.ui", [])
.provider('LayoutManager', [function () {
'use strict';
var appLayout = $('body').layout(),
moduleNavLayout = $('.module-nav-container').layout(),
moduleLayout = $('.module-content-container').layout();
return {
$get: function () {
return {
ApplicationLayout: appLayout,
ModuleNavigatonLayout: moduleNavLayout,
ModuleContentLayout: moduleLayout
}
}
}
}]);
Then I identify the module (app.ui) as a dependency of the "app" (ListMgrApp) I want to use it in.
angular.module("ListMgrApp", ["ui.router", "app.services", "app.ui"])
Then I inject (is that the correct terminology?) the specific provider (LayoutManager) into the application ...
angular.module("ListMgrApp", ["ui.router", "app.services", "app.ui"]).
config(['$stateProvider', 'LayoutManager',
function ($stateProvider, layout) {
'use strict';
// initialization code goes here
}]);
While it appears that the code inside the LayoutManager provider DOES execute, which I believe is due to it being included as a dependency for the app, I am still getting the following error from the application when it runs.
Uncaught Error: [$injector:modulerr] Failed to instantiate module ListMgrApp due to:
Error: [$injector:unpr] Unknown provider: LayoutManager
I have verified that the source code for all required files are being successfully down loaded.
What am I missing?
RESOLUTION
Thanks elclanrs for the answer! I just wanted to add what exactly I updated to make it work.
I added "Provider" to the name of the provider (LayoutManager) in the config() method of the app (ListMgrApp). I had originally thought I was supposed to also change the name of "LayoutManager" in the provider code but misread the original solution comment. Only change the name in the app config() method. I thought I would point it out here just in case someone else was a "skimmer" and missed it.
angular.module("ListMgrApp", ["ui.router", "app.services", "app.ui"]).
config(['$stateProvider', 'LayoutManagerProvider',
function ($stateProvider, layout) {
'use strict';
// initialization code goes here
}]);

How do I get an Angularjs module and invoke .value on it?

I'm trying to use a vertxbus module. It's configuration is set using .value.
How do I update these configuration values from my 'appModule'?
To my understanding
angular.module('knalli.angular-vertxbus')
should return a reference to the module and .value should change the injected value.
I've created a jsFiddle and here is the js used:
'use strict';
var testmodule = angular.module('TestModule', []).value('key', 'module').value('key', 'module2');
testmodule.factory('MyService', ['key', function (key) {
return key;
}]);
var module = angular.module('myApp', ['TestModule'])
.run(function () {
testmodule.value('key', 'run1');
angular.module('TestModule').value('key', 'run2');
}).controller('MyCtrl', ['$scope', 'MyService', 'key', function ($scope, MyService, key) {
$scope.value = MyService;
$scope.key = key;
}]);
I would expect a result of run2 or at least run1 but module2 is returned. Why?
The actual idea behind .value, .constant or more generally .config is the possibility to change how the modules' components should constructed themselves.
In that case, the angular-translate's vertxBus component will invoke implicitly a SockJS instance which connects implicitly to a server. That means the configuration settings must be known before the application will be running. Because of this, there are two phases (see answer by Ilan Frumer): config and run.
However, because .value and .config are too static and could collide with other components, the angular module angular-vertxbus has an own provider for all the settings (since version 0.5: https://github.com/knalli/angular-vertxbus/releases/tag/v0.5.0).
Starting with 0.5, this could be look like this:
var module = angular.module('app', ['knalli.vertx-eventbus']);
module.config(function(vertxEventBus) {
vertxEventBus.useDebug(true).useUrlPath('/eventbus');
});
Just like the others, the provider is only available in the config-phase.
Disclaimer: I'm the author of angular-vertxbus
$provide shorthands
Under the hood, constant & value & service & factory & provider are all shorthands for the $provide service which is available only during the configuration phase.
From angular.js documentation:
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.
Run blocks - get executed after the injector is created and are used to kickstart the application. Only instances and constants can be injected into run blocks. This is to prevent further system configuration during application run time.
your case:
You cannot register providers (in your case value) inside run blocks because they are only available during the configuration phase.
Read more about modules and $provide

AngularJS Where should i set or store the configuration for a modul

i've made an reusable module for angularJS. The module is manipulating templates inside the run function. Before its gets fully initialized i need to set various properties. In which function should i expose this properties ?
In my Angular-1.2 apps to configure the ngRoute's service I use a config block like this
app.config(function ($routeProvider) {
$routeProvider....
});
I think you could do the same by adding a service provider into your module.
Another solution would be to make your run block depend on a constant that would be defined from your application.
// In your module
foo.run(function (fooConfig) {
var url = fooConfig.url;
...
});
// In your app
app.constant('fooConfig', { url: ... });
Both solutions are demoed here : http://jsfiddle.net/JQ4Gm/
I should expose my "properties" inside a provider and access them inside config. Usage of provider.

Categories

Resources