Can't resolve variables from ui-router resolve object into controller - javascript

I'm using ui-router in my app.
app.config(['$stateProvider', function($stateProvider){
$stateProvide.state('State1', {
url:'/State1',
resolve: {
data: function(Service){
return Service.init();
}
},
views: {
"header": {
templateUrl: 'views/header.tpl.html'
},
"center":{
templateUrl: 'views/center.tpl.html'
},
"footer": {
templateUrl: 'views/footer.tpl.html'
}
}
}
})
}
]);
I tried to load a json file from the server and resolve it in the router.
In the resolve object, I call to my service that is responsible for returning promise.
Service.js:
app.service("Service", ['$rootscope', '$http', function($rootscope, $http){
var promise;
this.init = function(){
promise = this.loadData();
return promise;
};
this.loadData = function(){
var url = "users/getData/json.json";
return $http.get(url).then(function(response){
return response.data;
}, function(error){
alert(error);
})
};
}])
center.tpl.html:
<aside id="first-item" ng-controller="FirstController as firstController">
<first-directive>
</aside>
<aside id="second-item" ng-controller="SecondController as secondController">
<second-directive>
</aside>
This is the controller to which I would like to get the resolved data.
FirstController.js:
app.controller('FirstController', ['$scope', 'data', function($scope, data){
this.myData = data;
}]);
I got the next error: Unknown provider: dataProvider < - data. Why?

since promise is never resolved before returning. try this one.
app.service("Service", ['$rootscope', '$http', function($rootscope, $http){
var promise;
this.init = function(){
return this.loadData();
};
this.loadData = function(){
var url = "users/getData/json.json";
return $http.get(url).then(function(response){
return response.data;
}, function(error){
alert(error);
})
};
}])

From the ui-router docs:
// The controller waits for every one of the above items to be
// completely resolved before instantiation. For example, the
// controller will not instantiate until promiseObj's promise has
// been resolved. Then those objects are injected into the controller
// and available for use.
It is cleared that the resolved variables will only be available in the controllers defined in the state config. You can not resolve those variables in a normal controller and you are trying to use data from your state config in your controller and hence you are getting that error.
But, to get those data in your FirstController, you can do like this:
app.config('$stateProvider', ['$rootScope', function ($stateProvider) {
$stateProvide.state('State1', {
url: '/State1',
resolve: {
data: function (Service) {
var data = Service.init();
$rootScope.$broadcast("dataReceivedFoo", {data: data});
return data;
}
},
views: {
"header": {
templateUrl: 'views/header.tpl.html'
},
"center": {
templateUrl: 'views/center.tpl.html'
},
"footer": {
templateUrl: 'views/footer.tpl.html'
}
}
})
}]);
And, then read in your controller:
app.controller('FirstController', ['$scope' function($scope){
$scope.$on("dataReceivedFoo", function(response) {
$scope.myData = response.data;
})
}]);
Basically, we are broadcasting the data from your state configuration and then receiving in your FirstController.

I think adding controller: 'FirstController' line to state will solve the problem.
app.config(['$stateProvider', function($stateProvider){
$stateProvide.state('State1', {
url:'/State1',
controller: 'FirstController',
resolve: {
data: function(Service){
return Service.init();
}
},
views: {
"header": {
templateUrl: 'views/header.tpl.html'
},
"center":{
templateUrl: 'views/center.tpl.html'
},
"footer": {
templateUrl: 'views/footer.tpl.html'
}
}
}
})
}
]);
Resolve
You can use resolve to provide your controller with content or data
that is custom to the state. resolve is an optional map of
dependencies which should be injected into the controller.
If any of these dependencies are promises, they will be resolved and
converted to a value before the controller is instantiated and the
$stateChangeSuccess event is fired.
The resolve property is a map object. The map object contains
key/value pairs of:
key – {string}: a name of a dependency to be injected into the
controller.
factory - {string|function}: If string, then it is an
alias for a service. Otherwise if function, then it is injected and
the return value is treated as the dependency. If the result is a
promise, it is resolved before the controller is instantiated and its
value is injected into the controller.
https://github.com/angular-ui/ui-router/wiki

Related

Injecting into lazy loaded AngularJS Controller when using ocLazyLoad

I started using ocLazyload to lazy load few of my AngularJs controllers. I have used it along with the $routeProvider like this
$routeProvider.when("/login", {
templateUrl: "login.html",
resolve: {
loadCtrl: ['$ocLazyLoad', function($ocLazyLoad) {
return $ocLazyLoad.load('LoginCtrl.js');
}]
}
}).
and this works fine.
I have another route definition which uses resolve property to resolve few items before loading the controller.
when("/dashboard", {
templateUrl: "dashboard.html",
controller: "DashboardCtrl",
resolve: {
profileData: getProfile,
count : getCount
}
}).
Now I want to lazy load this controller too, and I tried it like this
when("/dashboard", {
templateUrl: "dashboard.html",
resolve: {
profileData: getProfile,
count : getCount,
loadCtrl: ['$ocLazyLoad', function($ocLazyLoad) {
return $ocLazyLoad.load(['DashboardCtrl.js']);
}]
}
}).
The page loads in this case, but the profileData and count doesn't get injected into the controller. The controller definition is as given below.
var app = angular.module('gt');
app.controller('DashboardCtrl', ['$scope', '$rootScope', 'profileData', 'count',
function($scope, $rootScope, profileData, count) {
...
}]);
On debugging, I realise that the getProfile and getCount method get's called, but it happens asynchronously and the controller also lazy loads without waiting for these methods. How do I inject and lazy load at the same time? Can I use promises to resolve this in any way?
I am using AngularJS 1.3.10 & ocLazyLoad 1.0.5 versions
getProfile function for reference
var getProfile = function($q, $http, Profile, localStorageService) {
var deferred = $q.defer();
if (!localStorageService.get("loggedInUser")) {
$http.post('/loggedin').success(function(user) {
if (user) {
localStorageService.set("loggedInUser", user.email)
Profile.get(localStorageService.get("loggedInUser"), function(profileData) {
if (profileData) {
deferred.resolve(profileData);
} else {
deferred.resolve();
}
});
} else {
deferred.reject();
}
});
} else {
Profile.get(localStorageService.get("loggedInUser"), function(profileData) {
if (profileData) {
deferred.resolve(profileData);
} else {
deferred.resolve();
}
});
}
return deferred.promise;
}
getProfile.$inject = ["$q", "$http", "Profile", "localStorageService"];
I could get this working with the following configuration of $routeProvider
when("/dashboard", {
templateUrl: "dashboard.html",
controller :"DashboardCtrl"
resolve: {
profileData: getProfile,
count : getCount,
loadCtrl: ['$ocLazyLoad', function($ocLazyLoad) {
return $ocLazyLoad.load(['DashboardCtrl.js']);
}]
}
}).
where DashboardCtrl is the controller defined in DashboardCtrl.js
does getProfile and getCount return a promise? I would guess this is the problem as this is required. Every object put in resolve should return a promise. see the documention
If you need your resolves to happen in a specific order, you can inject them into another resolve, like this:
when("/dashboard", {
templateUrl: "dashboard.html",
resolve: {
profileData: getProfile,
count : getCount,
loadCtrl: ['$ocLazyLoad', 'profileData', 'count', function($ocLazyLoad, profileData, count) {
return $ocLazyLoad.load(['DashboardCtrl.js']);
}]
}
}).

Angular stateprovider resolve not passing data to controller

So I have a problem with angular ui.router, which apparently isn't passing the data from resolve to controller. I have the following state set up:
$stateProvider
.state('myState', {
url: "/myUrl",
templateUrl: "myTemplate",
controller: 'myController',
resolve: {
randomData: function($q, $sails) {
var defer = $q.defer();
$sails.get("/me")
.success(function(data) {
console.log(data) // prints out actual data
defer.resolve(data);
})
return defer.promise;
}
}
and in myController I basically have
myApp.controller('myController', [
'$scope', function ($scope, randomData) {
console.log("randomData:" + randomData)
// prints out 'randomData: undefined'
}
])
According to every doc, stackoverflow post and tutorial, this piece of code should work, but it keeps printing undefined. Does anyone have an idea why this isn't working?
You forgot to inject the data in the array notation:
myApp.controller('myController', [
'$scope', 'randomData', // <-- this one
function ($scope, randomData) {
console.log("randomData:" + randomData);
}
])

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()
}
}
});

How to load data into service without resolve

I want to fetch some data from a server and copy it to a service before a routechange is completed:
when(
'/detail/:id',
{
templateUrl: './partials/views/detail.php',
controller: 'detailCtrl',
resolve: {
init: function($route,$q,shipmentn){
var deffered = $q.defer();
shipmentn.getSingle($route.current.params.id).then(function(promise){
shipmentn.data = promise.data;
deffered.resolve(promise);
});
return deffered.promise;
}
}
}
)
As you can see, i am doing this inside the then() function to make sure that the request has been completed.
What bothers me, is that i have to inject another dependency (init) into my controller and have to return a promise which is never used.
How could i avoid this?
A trick might be to pass the service you want in your controller to the resolve function of the defered object, after the service itself finished its loading:
when('/detail/:id', {
templateUrl: './partials/views/detail.php',
controller: function($scope,service){}, //<-- Controller with the service dependency
resolve: {
service: function($route,$q,shipmentn){
var deffered = $q.defer();
shipmentn.getSingle($route.current.params.id).then(function(promise){
shipmentn.data = promise.data;
deffered.resolve(shipmentn); //<-- the deffered resolves the service
});
return deffered.promise;
}
}})
regards
If you are only looking to send data to the controller, you don't need to store it in the service, it can just be resolved to the controller:
router:
when('/detail/:id', {templateUrl: './partials/views/detail.php', controller: 'detailCtrl',resolve: {
shipment: function($route,$q,shipmentn){
return shipmentn.getSingle($route.current.params.id));
}
}})
service:
getSingle: function(id){
var deferred = q.defer();
$http.get(...).then(function(response){
deferred.resolve(response.data);
}
return deferred.promise;
}
controller:
module.controller('detailController', ['shipment', function(shipment){
...
}]);
On the controller, shipment will be whatever is returned from response.data in the http.get.

How can you manually inject route resolve data inside a controller?

I have 2 routes that share a controller, one needs data resolved prior to the view loading, and the other does not need the resolved data.
Routing segment example:
...
when('/users', {
controller: 'UsersCtrl',
templateUrl: '/partials/users/view.html',
resolve: {
resolvedData : ['Accounts', function(Accounts) {
return Accounts.get();
}]
}
}).
when('/users/add', {
controller: 'UsersCtrl',
templateUrl: '/partials/users/add.html'
})
...
Controller example:
app.controller('UsersCtrl', ['$scope', 'Helper', 'resolvedData',
function($scope, Helper, resolvedData) {
// this works for the first route, but fails for the second route with
// unknown "resolvedDataProvider"
console.log(resolvedData);
}]);
Is there any way I can get the resolvedData in the controller without explicitly using the resolve name as a dependency? So a check can be performed?
Using the $injector does not work. I would like to do something similar to:
if ($injector.has('resolveData')) {
var resolveData = $injector.get('resolveData');
}
However this does not work even for the route that has the resolveData set ('/users'):
app.controller('UsersCtrl', ['$scope', 'Helper', '$injector',
function($scope, Helper, $injector) {
// this does not work -> fails with the unknown "resolvedDataProvider" as well
$injector.get('resolvedData');
}]);
Can this be done in angularjs? Or should I just create a new controller?
Thank you.
Looks like I figured out another way to go. The resolved data is part of the $route. So you can access it using:
app.controller('UsersCtrl', ['$scope', '$route', 'Helper',
function($scope, $route, Helper) {
if ($route.current.locals.resolvedData) {
var resolvedData = $route.current.locals.resolvedData;
}
}]);
If the other route doesn't need it, just inject undefined on that route:
router:
when('/users', {
controller: 'UsersCtrl',
templateUrl: '/partials/users/view.html',
resolve: {
resolvedData : ['Accounts', function(Accounts) {
return Accounts.get();
}]
}
}).
when('/users/add', {
controller: 'UsersCtrl',
templateUrl: '/partials/users/add.html',
resolve: {
resolvedData: function() {
return undefined;
}
}
})
controller:
app.controller('UsersCtrl', ['$scope', 'Helper', 'resolvedData',
function($scope, Helper, resolvedData) {
if(resolvedData){
//set some scope stuff for it
} else {
//do what you do when there is no resolvedData
}
}]);

Categories