$routeProvider and resolve : more complex factory passed as string - javascript

Please check this Plunk
What I'm doing is I have following AngularJS module:
var synergy_module = angular.module('synergy', ['ngRoute'])
.config(function($provide, $routeProvider, $httpProvider) {
$routeProvider.when('/', {
template: '<div>{{here}}</div>',
controller: SpecificationCtrl,
resolve: {
w: "myFct"
}
});
$routeProvider.otherwise({
redirectTo: '/'
});
});
synergy_module.factory("myFct", ["$http",
function($http) {
return $http.get("http://api.openweathermap.org/data/2.5/weather?q=London,uk");
}
]).factory("my.Fct", ["$http",
function($http) {
var o = {
getWeather: function() {
return $http.get("http://api.openweathermap.org/data/2.5/weather?q=London,uk");
}
}
return o;
}
])
function SpecificationCtrl($scope, w) {
$scope.here = w.data;
}
This works fine as the myFct factory has a simple name and returns $promise but it is not very useful. My factories look more like my.Fct:
they have dots in names (aka "packages") and
return object with methods (these methods return $promise)
How can I pass this "complex" my.Fct factory to the resolve as a string and specify which method of the factory should be used when resolving?
Update:
OK, based on some more search, this works for me:
resolve: {w: ["my.Fct",function (myFct) {
return myFct.getWeather();
}]}
But it is much more code, using the string seems more readable to me...

Related

i18n angular fails after minification

I am using ui bootstrap and use tmhDynamicLocaleProvider to get the correct translations for days,month etc in my datepicker.
It works all fine as long as I load the locale documents from online. If I load documents locally (I need to do it locally), it doesn't load the correct translations after minification.
My code looks as follows
app.config(['tmhDynamicLocaleProvider', function(tmhDynamicLocaleProvider) {
tmhDynamicLocaleProvider.localeLocationPattern('app/datepickerLocale/locale.{{locale}}.js');
}])
app.config(....{
$provide.decorator('uibDatepickerDirective', ['$delegate', function($delegate) {
angular.forEach($delegate, function (directive) {
var originalCompile = directive.compile;
var originalLink = directive.link;
if (originalCompile) {
directive.compile = function () {
return function (scope) {
scope.$on('$localeChangeSuccess', function () {
scope.move(0);
});
originalLink.apply(this, arguments);
};
}
}
});
return $delegate;
}]);
})
I resolve it in a state
.state('main', {
url: '/{language:[a-z]{2}}',
templateUrl: 'app/main/main.html',
controller: 'MainCtrl',
controllerAs: 'mainCtrl',
resolve: {
localeLanguage: ['resolveService', '$stateParams', function(resolveService, $stateParams){
resolveService.resolveLocale($stateParams)
}]
}
})
the service looks as follows
resolveLocale: function(stateParams){
var deferred = $q.defer();
if(stateParams.language){
tmhDynamicLocale.set(stateParams.language);
deferred.resolve(1);
} else {
deferred.resolve(2);
}
return deferred.promise;
}
it all works fine until minification. After that I obtain the error, that
the script was not able to be obtained. (GET error).
I assume that the while invoking the function the script locale.en.js has not been loaded yet. Or am I wrong here?
How can I solve this?

Router resolve will not inject into controller

I have tried everything to get ui-router's resolve to pass it's value to the given controller–AppCtrl. I am using dependency injection with $inject, and that seems to cause the issues. What am I missing?
Routing
$stateProvider.state('app.index', {
url: '/me',
templateUrl: '/includes/app/me.jade',
controller: 'AppCtrl',
controllerAs: 'vm',
resolve: {
auser: ['User', function(User) {
return User.getUser().then(function(user) {
return user;
});
}],
}
});
Controller
appControllers.controller('AppCtrl', AppCtrl);
AppCtrl.$inject = ['$scope', '$rootScope'];
function AppCtrl($scope, $rootScope, auser) {
var vm = this;
console.log(auser); // undefined
...
}
Edit
Here's a plunk http://plnkr.co/edit/PoCiEnh64hR4XM24aH33?p=preview
When you use route resolve argument as dependency injection in the controller bound to the route, you cannot use that controller with ng-controller directive because the service provider with the name aname does not exist. It is a dynamic dependency that is injected by the router when it instantiates the controller to be bound in its respective partial view.
Also remember to return $timeout in your example, because it returns a promise otherwise your argument will get resolved with no value, same is the case if you are using $http or another service that returns a promise.
i.e
resolve: {
auser: ['$timeout', function($timeout) {
return $timeout(function() {
return {name:'me'}
}, 1000);
}],
In the controller inject the resolve dependency.
appControllers.controller('AppCtrl', AppCtrl);
AppCtrl.$inject = ['$scope', '$rootScope','auser']; //Inject auser here
function AppCtrl($scope, $rootScope, auser) {
var vm = this;
vm.user = auser;
}
in the view instead of ng-controller, use ui-view directive:
<div ui-view></div>
Demo
Here is how I work with resolve. It should receive promise. So I create service accordingly.
app.factory('User', function($http){
var user = {};
return {
resolve: function() {
return $http.get('api/user/1').success(function(data){
user = data;
});
},
get: function() {
return user;
}
}
});
This is main idea. You can also do something like this with $q
app.factory('User', function($q, $http){
var user = {};
var defer = $q.defer();
$http.get('api/user/1').success(function(data){
user = data;
defer.resolve();
}).error(function(){
defer.reject();
});
return {
resolve: function() {
return defer.promise;
},
get: function() {
return user;
}
}
});
These are almost identical in action. The difference is that in first case, service will start fetching date when you call resolve() method of service and in second example it will start fetch when factory object is created.
Now in your state.
$stateProvider.state('app.index', {
url: '/me',
templateUrl: '/includes/app/me.jade',
controller: function ($scope, $rootScope, User) {
$scope.user = User.get();
console.log($scope.user);
},
controllerAs: 'vm',
resolve: {
auser: function(User) {
return User.resolve()
}
}
});

Extending a service provider (provider)

I'm trying to extend a service provider; more specifically $routeProvider from the ngRoute module
my extended provider looks like this:
angular
.module('myapp')
.provider('customRoute', customRoute)
function customRoute($routeProvider) {
var extendedProvider = angular.extend({}, $routeProvider, {
// Customization here
});
// These will print almost identical objects
console.log(extendedProvider);
console.log($routeProvider);
this.$get = function() {
return extendedProvider;
}
}
The config of the routes looks something like this:
angular
.module('myapp')
.config(Routes);
function Routes(customRouteProvider, $routeProvider) {
/* This will print an object that looks the same as
the ones printed in the provider definition */
console.log($routeProvider);
/* This object does not look like the $routeProvider any more :( */
console.log(customRouteProvider);
customRouteProvider
.when('/', {
templateUrl: 'path/to/some/template.html',
controller: 'SomeController',
})
.otherwise({
redirectTo: '/'
});
}
I've read this thread:
how can i extend a service
But they only talk about extending the "factory"-service
Can someone explain what is happening here? Help is much appreciated.
In order to extend $routeProvider your customRoute function must return extended provider object:
function customRoute($routeProvider) {
var extendedProvider = angular.extend({}, $routeProvider, {
// Customization here
mergeRoutes: function() {}
});
return extendedProvider;
}
After that you could for example you new custom method customRouteProvider.mergeRoutes:
customRouteProvider
.when('/', {
templateUrl: 'path/to/some/template.html',
controller: 'SomeController',
})
.otherwise({
redirectTo: '/'
})
.mergeRoutes({ ... });
Demo to play: http://plnkr.co/edit/mht94RFLScz2jHCwPcai?p=preview
For your purposes you don't need to mess with this.$get, you just need to add some new methods to $routeProvider. this.$get returns a service object instance/constructor for corresponding provider service ($route in case of $routeProvider).

Injecting services/constants into provider in Angularjs

The problem here is I am able to access the getRoutes(), but I am unable to access the injected constant -"configuration". What am I missing? Thanks.
(function () {
'use strict';
var app = angular.module('app');
app.constant('configuration', {
PARTIAL_PATH: "/app/components/partials"
});
app.module('app', [
'routeService'
]);
var routeServiceModule = angular.module('routeService', ['common']);
routeServiceModule.provider('routeConfig',function () {
this.getRoutes = function () {
return [
{
url: '/login',
config: {
title: 'admin',
templateUrl: 'app/components/login/login.html'
}
}, {
url: '/',
config: {
templateUrl: 'app/components/dashboard/dashboard.html',
title: 'Dashboard'
}
}
];
};
this.$get = ['configuration', function (configuration) {
var service = {
getRoutes: getRoutes(),
configuration: configuration.PARTIAL_PATH
};
return service;
}];
app.config(['$routeProvider', 'routeConfigProvider', function ($routeProvider, routeConfigProvider) {
//Unable to get the configuration value
console.log(routeConfigProvider.configuration);
//Console is returning as "undefined"
routeConfigProvider.getRoutes().forEach(function(r) {
$routeProvider.when(r.url, r.config);
});
$routeProvider.otherwise({ redirectTo: '/' });
}
]);
})();
Created a plunkr demo : http://plnkr.co/edit/2TIqgxMxBJEPbnk2Wk6D?p=preview
(Regarding your last comment, with the plnkr)
The result is expected.
At config time (within app.config() ), you access raw providers, as you defined them, which allows you to call "private" methods or fields (testItem1) and to configure it for run time use. "private" because they won't be accessible at run time.
At run time (within app.run() and the rest of your app), when you ask for a dependency for which you wrote a provider, the angular injector hands you the result of the $get method of your provider, not the provider itself, so you can't access the "private" function.
This page was my path to enlightenment : AngularJS: Service vs provider vs factory
I think you may be over complicating the route stuff. You may have a very good reason for it but as I do not know it may I suggest keeping it simple with something more like this:
MyApp.config(function ($routeProvider) {
$routeProvider.when('/home', {
templateUrl: 'home.html',
controller: 'HomeController',
activeTab: 'home'
})
};
MyApp.controller('HomeController', function ($route) {
console.log($route.current.activeTab);
});
I would be interested in knowing why you may not able to use this routing pattern or purposely chose something different.
I think it has to do with the way you are creating your initial module. Try this:
var app = angular.module('app', []);
app.constant('configuration', {
PARTIAL_PATH: "/app/components/partials"
});
var routeServiceModule = angular.module('routeService', ['app']);

Injecting Dependencies in config() modules - AngularJS

Currently in app.js i have the following routes:
var gm = angular.module('gm', ['gm.services','gm.directives','gm.filters','gm.controllers','ngSanitize']);
gm.config(['$routeProvider', 'Path', function($routeProvider, Path) {
$routeProvider.when('/login', {
templateUrl: Path.view('application/authentication/login.html'),
controller: 'authController'
});
$routeProvider.when('/dashboard', {
templateUrl: Path.view('application/dashboard/index.html'),
controller: 'dashboardController'
});
$routeProvider.otherwise({
redirectTo: '/login'
});
}]);
I'm trying to inject the Path dependency as you can see. Although i get an error saying it can't find this provider. I think this is because config module providers are executed first before anything else. below is my Path provider definition in "services.js"
gm.factory("Path", function() {
return {
view: function(path) {
return 'app/views/' + path;
},
css: function(path) {
return 'app/views/' + path;
},
font: function(path) {
return 'app/views/' + path;
},
img: function(path) {
return 'app/views/' + path;
},
js: function(path) {
return 'app/views/' + path;
},
vendor: function(path) {
return 'app/views/' + path;
},
base: function(path) {
return '/' + path;
}
}
});
how can i inject this provider into a config module?
angular.config only accepts Providers
every service, factory etc are instances of Provider
So to inject a service in config you just need to call the Provider of the service by adding 'Provider' to it's name.
angular.module('myApp')
.service('FooService', function(){
//...etc
})
.config(function(FooServiceProvider){
//...etc
});
According to the angularjs Provider documentation
... if you define a Factory recipe, an empty Provider type with the $get method set to your factory function is automatically created under the hood.
So if you have a factory (or service) such as:
.factory('myConfig', function(){
return {
hello: function(msg){
console.log('hello ' + msg)
}
}
})
You first need to invoke your factory using the $get method before accessing the returned object:
.config(function(myConfigProvider){
myConfigProvider
.$get()
.hello('world');
});
In .config you can only use providers (e.g. $routeProvider). in .run you can only use instances of services (e.g. $route). You have a factory, not a provider. See this snippet with the three ways of creating this: Service, Factory and Provider
They also mention this in the angular docs https://docs.angularjs.org/guide/services
You should use constant for that, because it's the only thing you can inject in the config phase other than providers.
angular.module("yourModule").constant("paths", {
base: function(){ ... }
});
This discussion helped me when I was trying to figure out the same thing, basically
$routeProvider.when('/', {
templateUrl:'views/main.html',
controller:'MainController',
resolve: {
recentPosts: ['$q', 'backendService', function($q, backendService){
var deferred = $q.defer();
backendService.getRecentPosts().then(
function(data) {
var result = data.result;
deferred.resolve(result);
},
function(error) {
deferred.reject(error);
}
);
return deferred.promise;
}]
}
})

Categories