app.js
angular
.module('yelp',['ngRoute']) //creating module
.config(config);
config.$inject=['$routeProvider','YelpService']; //YelpService is unknown here
function config($routeProvider){
$routeProvider
.when('/restaurants',{
templateUrl:'partials/restaurants.html',
controller:'RestaurantsController',
controllerAs:'rstrntsCtrl',
resolve:{
getRestaurants:getRestaurants
}
});
}
function getRestaurants(YelpService){
// i need route params here
}
YelpService.js
angular
.module('yelp') //using already created module
.factory('YelpService',YelpService);
YelpService.$inject=['$http'];
function YelpService($http){
var factory={
}
return factory;
}
I loaded app.js file first and then yelpService.js file. Since service is loaded later i think it is not available to inject. If i load service file first, it causes an error, since the module in created in config file. How to overcome this. Moreover in config file how to get routeparams in getRestaurants method
For yelp use the provider call YelpServiceProvider.
For the resolve just wrap in a function and inject the desired functionality.
angular
.module('yelp',['ngRoute']) //creating module
.config(config);
config.$inject=['$routeProvider','YelpServiceProvider']; //YelpService is unknown here
function config($routeProvider){
$routeProvider
.when('/restaurants',{
templateUrl:'partials/restaurants.html',
controller:'RestaurantsController',
controllerAs:'rstrntsCtrl',
resolve:{
restaurants:function($routeParams, YelpService) {
return getRestaurants($routeParams, YelpService);
}
}
});
}
function getRestaurants($routeParams, YelpService){
// i need route params here
}
Resolve is used to, well, resolve your data. So the left hand assignment would not be a function name as you have perhaps meant but the variable list of items (a promise actually).
Then in your controller after having injected 'restaurants':
this.restaurants; // or $scope.restaurants if you are not using controllerAs
Related
I have an existing application that uses a MapProvider like so:
mapModule.factory('MapProvider', ['$injector', function($injector) {
return $injector.get('GoogleMapsService');
}]);
This MapProvider is used extensively across the application and is injected into various other controllers and services (rightly or wrongly).
I now need to add a BaiduMapsService, which I have been able to get working as a test with:
mapModule.factory('MapProvider', ['$injector', function($injector) {
if(true) {
return $injector.get('GoogleMapsService');
} else {
return $injector.get('BaiduMapsService');
}
}]);
And flipping the if value accordingly. (Both of these services are using a TypeScript interface, so have the same methods). Now, I need to add a $http call to the API, which will return which map to use, based on the provided data. How can I make my factory asynchronous, without having to change all my MapProvider.someCallHere() calls to MapProvider.then(m => m.someCallHere()).
Ideally, when MapProvider is injected across my application, it will be able to resolve using the async data (only once), and then inject the necessary service afterwards.
Alternatively, is there a way to defer / delay loading Angular at all, until I make an API call and set some global data somewhere?
Thanks.
You can postpone the application bootstrap (also, don't use ng-app, do it manually) until you get data from server. I've answered this before on this question but each case has its own specific details.
I usually see a config value being declared on the app before the application gets bootstraped, this is very useful for multi-tenant apps. So that this preference values can be used in the whole app as an injected provider.
For example:
var app = angular.module('app', []);
// retrieve the $http provider
var ngInjector = angular.injector(["ng"]);
var $http = ngInjector.get("$http");
// load config function. Returns a promise.
function loadConfig(){
return $http.get("/config.json").then(function(response) {
// declare the configuration value on your app
app.constant("Config", response.data);
}, function(err) {
console.error("Error loading the application config.", err)
});
}
// Call loadConfig then bootstrap the app
loadConfig().then(function () {
angular.element(document).ready(function() {
angular.bootstrap(document, ["app"]);
});
});
Finally from your factory, you can use the Config constant to retrieve the preferred map.
mapModule.factory('MapProvider', ['$injector', 'Config', function($injector, Config) {
if(Config.preferedMap == 'GoogleMap') {
return $injector.get('GoogleMapsService');
} else {
return $injector.get('BaiduMapsService');
}
}]);
Only way I can think is to hold initialize whole angular (and modules) until you got your "config" (and set is as global variable).
I'm creating an app using angularjs, I have two controllers, and one service (all in separated files). My problem is that I can't inject the service in the controllers. I used the service in the same file as the controller and it worked, but I want it in separated files to share data between controllers.
This is one of my controllers
var app = angular.module('cloudpear', ['ngRoute','ServiceUser']);
app.controller('login',['$scope','$location','$window', function(ServiceUser,$scope,$location,$window){
.....
}]);
This is my service
var app = angular.module('service', []);
app.factory('ServiceUser',function(){
var user=[];
return{
add: add,
get: get
}
function add(item){
user.push(item);
}
function get(){
return user;
}
return user;
});
I get this error:
Uncaught Error: [$injector:modulerr] http://errors.angularjs.org/1.6.4-build.5327+sha.233f47b/$injector/modulerr…20(https%3A%2F%2Fcode.angularjs.org%2Fsnapshot%2Fangular.min.js%3A22%3A179)
Can you tell me what I'm doing wrong, I've searched everywhere and tried multiple solution, but nothing worked!
As you have define a separate module for service, you need to inject service module in the cloudpear module declaration.
var app = angular.module('cloudpear', ['ngRoute','service']);
//^^^^^^^^^^ instead of ServiceUser
Then need to inject service in controller
app.controller('login',['ServiceUser', '$scope','$location','$window',function(ServiceUser, $scope,$location,$window){
I want to know what's that proper way to handle the business logic in terms of loading the appropriate views and controllers.
I have a bunch of factories to load resources, read and write user progress (to a local file) and so forth. The user will spend no more than several seconds on a given view (there are 6-7 different ones in total) and will switch to another one with dynamically loaded resources based on his progress.
My current idea is to have a service/factory that keeps track of progress, which is loaded on the index page and then every controller sends a request to it, once it's finished. Thereafter the service changes the $state and loads the appropriate data.
I'm building my first app in AngularJS and I have tried to search StackOverflow and Google, but I still can't figure out how to approach this problem.
Even pointing me to the right direction or reading material would be greatly appreciated.
If you are creating AngularJs app first time then follow simple steps.
1. Create one index.JSP file where you should run your app using ng-app directive and add all scripts and files.
2. create one Js file app.js.
add all module name in app.js and run your js using .run method.
3. maintain services, controllers and filters, directives, templates seperately in different folders and in different files.
And dont forget to add module name in app.js and add path in index.jsp
4. In your services files write only sharing business logic.
All other business logic related to particular file write it in controller.
Here your are maintaining ajax calls so dont meshup it with controllers.
.Service
.factory('angularService', function () {
return {
// Write business logic
}
})
Declare $starteProvider and define .states in your controller.
For ex.
$stateProvider.state('xyz_state', function () {
// add url,
// templateUrl,
// controller
})
.controller('myFirstController', function () {
// Add your business logic
// scope variables
});
6. Maintain view pages separately.
7. Maintain Directives and Filters separately.
In angular views are by default loaded through ajax call when it is required. If you want to do the same in controller then use require.js. which will load the controller dynamically when it is required. In require.js you can also specify your other dependency library for every view & controller which will loaded through ajax when view is called.
index.html
<script data-main="js/main.js" type="text/javascript" src="js/require.js"></script>
main.js
require.config({
urlArgs: 'v=1.0',
});
require(
[
'app'
],
function() {
angular.bootstrap(document, ['UConnect']); //Add your module
}
);
app.js
'use strict';
define([],function() {
var app = angular.module('UConnect',['ngRoute','ngAnimate','ui.materialize','yaru22.angular-timeago']);
app.config(['$routeProvider', '$controllerProvider', '$provide',function($routeProvider, $controllerProvider, $provide) {
// Register your component
app.register = {
controller: $controllerProvider.register,
factory: $provide.factory,
service: $provide.service
};
// Add resolver for load controller through require.js
function resolveController(dependencies) {
return {
load: ['$q', '$rootScope', function ($q, $rootScope) {
var defer = $q.defer();
require(dependencies, function () {
defer.resolve();
$rootScope.$apply();
});
return defer.promise;
}]
}
};
$routeProvider
.when("/Pages", {
templateUrl : "templates/Pages.html",
controller: 'PagesCtrl', // Add controller name.
resolve: resolveController(['controller/PagesCtrl']) // Call resolver to load controller.
})
.when("/ContactUs", {
templateUrl : "templates/ContactUs.html",
controller: 'ContactUsCtrl',
resolve: resolveController(['controller/ContactUsCtrl'])
})
;
$routeProvider.otherwise('/Pages');
}]);
angular.element(document).ready(function () {
angular.bootstrap(document, ['Uconnect']);
});
return app;
});
I usually use the $templateCache & store all my views there. if your on Node environment i'd suggest add ng-html2js to your build process.
as for controllers & other JS you should minify and concat all and load just that file in the beginning.
Wasn't sure what title to give this one but basically I have an authorization provider which I have created myself which need to be configured during the config phase to have a requireLogin() function which will be run at a later date. Like this...
// Configure $auth service
app.config(function ($authProvider, appConfig) {
$authProvider.setRequireLoginFunction(function($modal){
// Use the modal service
});
})
And this is the provider code
app.provider('$auth', function(){
var _requireLoginFn;
return({
setRequireLoginFunction: setRequireLoginFunction,
$get: instantiateAuth
});
function setRequireLoginFunction(fn){
_requireLoginFn = fn;
}
function instantiateAuth($http, $q) {
return({
requireLogin: requireLogin
});
function requireLogin() {
return _requireLoginFn.apply(undefined, arguments);
}
}
});
Sidenote: I'm using ng-annotate so don't use the array syntax of DI.
Anyway, as you can see, the function that is being stored in the config phase which will later be called using $auth.requireLogin.then(...) etc. needs access to the angular-ui modal service, but when I call the function later from inside the provider obviously the DI stuff isn't happening because all I've done is put $modal in the arguments of my function which isn't magic. I also can't put $modal in the dependencies of my provider because it's too early to do that, also my provider doesn't know what dependencies the function I pass in will need.
I feel the answer is probably to:
A) use the injector within the function I'm passing in to get access to $modal service or
B) Somehow run the function from inside the provider when called externally and somehow get all the supplied dependencies injected at runtime?
Sorry if I'm not able to explain this easily. I tried to make a JSfiddle but couldn't hack the code down easily.
The main question - is it possible? I tried with no luck..
main app.js
...
var app = angular.module('myApp', ['services']);
app.config(['customProvider', function (customProvider) {
}]);
...
provider itself
var services = angular.module('services', []);
services.provider('custom', function ($http) {
});
And I've got such error:
Uncaught Error: Unknown provider: $http from services
Any ideas?
Thanks!
The bottom line is:
You CANNOT inject a service into the provider configuration section.
You CAN inject a service into the section which initializes the provider's service.
Details:
Angular framework has a 2 phase initialization process:
PHASE 1: Config
During the config phase all of the providers are initialized and all of the config sections are executed. The config sections may contain code which configures the provider objects and therefore they can be injected with provider objects.
However, since the providers are the factories for the service objects and at this stage the providers are not fully initialized/configured -> you cannot ask the provider to create a service for you at this stage -> at the configuration stage you cannot use/inject services.
When this phase is completed all of the providers are ready (no more provider configuration can be done after the configuration phase is completed).
PHASE 2: Run
During run phase all the run sections are executed. At this stage the providers are ready and can create services -> during run phase you can use/inject services.
Examples:
1. Injecting the $http service to the provider initialization function WILL NOT work
//ERRONEOUS
angular.module('myModule').provider('myProvider', function($http) {
// SECTION 1: code to initialize/configure the PROVIDER goes here (executed during `config` phase)
...
this.$get = function() {
// code to initialize/configure the SERVICE goes here (executed during `run` stage)
return myService;
};
});
Since we are trying to inject the $http service into a function which is executed during the config phase we will get an error:
Uncaught Error: Unknown provider: $http from services
What this error is actually saying is that the $httpProvider which is used to create the $http service is not ready yet (since we are still in the config phase).
2. Injecting the $http service to the service initialization function WILL work:
//OK
angular.module('myModule').provider('myProvider', function() {
// SECTION 1: code to initialize/configure the PROVIDER goes here (executed during `config` phase)
...
this.$get = function($http) {
// code to initialize/configure the SERVICE goes here (executed during `run` stage)
return myService;
};
});
Since we are now injecting the service into the service initialization function, which is executed during run phase this code will work.
This might give you a little leverage:
var initInjector = angular.injector(['ng']);
var $http = initInjector.get('$http');
But be careful, the success/error callbacks might keep you in a race-condition between the app start and the server response.
This is an old question, seems we have some chicken egg thing going on if we want to rely on the core capability of the library.
Instead of solving the problem in a fundamental way, what I did is by-pass. Create a directive that wraps the whole body. Ex.
<body ng-app="app">
<div mc-body>
Hello World
</div>
</body>
Now mc-body needs to be initialized before rendering (once), ex.
link: function(scope, element, attrs) {
Auth.login().then() ...
}
Auth is a service or provider, ex.
.provider('Auth', function() {
... keep your auth configurations
return {
$get: function($http) {
return {
login: function() {
... do something about the http
}
}
}
}
})
Seems to me that I do have control on the order of the bootstrap, it is after the regular bootstrap resolves all provider configuration and then try to initialize mc-body directive.
And this directive seems to me can be ahead of routing, because routing is also injected via a directive ex. <ui-route />. But I can be wrong on this. Needs some more investigation.
In response to your question, "Any Ideas?", I would have respond with "yes". But wait, there's more!
I suggest just using JQuery in the config. For example:
var app = angular.module('myApp', ['services']);
app.config(['$anyProvider', function ($anyProvider) {
$.ajax({
url: 'www.something.com/api/lolol',
success: function (result) {
$anyProvider.doSomething(result);
}
});
}]);