RouteProvider resolve AngularJS - javascript

Here is my code :
Js:
angular.module('main', [])
.config(['$locationProvider', '$routeProvider',
function($locationProvider, $routeProvider) {
$routeProvider.when('/tables/bricks', {
controller: "myController",
resolve: {
"check" : function($location){
if(!$scope.bricks) {
$route.reload();
}
}
},
templateUrl: 'tables/bricks.html'
});
$routeProvider.otherwise({
redirectTo: '/tables/datatables'
});
}
])
.controller('myController', function($scope, $location, $http) {
var vm = this;
$scope.Bricks = function(){
$location.path('/tables/bricks');
};
vm.getbricks = function(n){
var url = n;
$http({
method: 'GET' ,
url: url,
})
.then(function successCallback(data) {
$scope.bricks = data.data;
console.log($scope.bricks);
}, function errorCallback(response) {
console.log(response);
console.log('error');
});
};
});
HTML:
<button ng-click="vm.getbricks(n.bricks_url);Bricks();"></button>
After click the button in html, my page goes into /tables/bricks, but nothing happend, because resolve probably is wrong. What I want - that i could go to /tables/bricks only then, when $scope.bricks exist, so only when vm.bricks() will be called.
Thanks for answers in advance!

I think your problem is that the vm.getbricks will always return something (in success or error handler), so will never be falsy, and you will always call the Bricks() constructor. try to return true on success callback and false in error callback.

$scope is for controllers, which it can't reach in the config. Instead, you should be returning something from a service, which will be called during your resolve. E.g. if(YourService.getbricks())
Solution: move your logic from a controller into a service. And make sure to return a value from it that can be checked in the config.
app.service('BrickService', function() {
this.getbricks = function(url) {
return $http.get(url) // return the Promise
.then(function(response) {
return response.data; // return the data
}, function(error) {
console.log(error);
});
};
});
With this you can inject the service into the config and run its function.
angular.module('main', [])
.config(['$locationProvider', '$routeProvider',
function($locationProvider, $routeProvider) {
$routeProvider.when('/tables/bricks', {
controller: "myController",
resolve: {
"check": function(BrickService) { // inject
if ( BrickService.getbricks() ) { // run its function
$route.reload();
}
}
},
templateUrl: 'tables/bricks.html'
});
$routeProvider.otherwise({
redirectTo: '/tables/datatables'
});
}
])
You can also use the loaded values in the controller after they have been resolved. For that, you would need to simply return it. So change the logic to this:
resolve: {
"check": function(BrickService) { // inject
var bricks = BrickService.getbricks(); // run its function
if ( bricks ) {
$route.reload();
}
return bricks; // return the result (note: it's not a Promise anymore)
}
}
Then you can inject this resolve into your controller:
.controller('myController', function($scope, $location, $http, check) {
var vm = this;
vm.bricks = check;
...
(Note check was added)

Related

UI router resolve do not work

Right now i am trying to make an Angular JS install application, to install a CMS. So i am trying to block access to a state (ui router), i am doing it with a resolve function. But the problem is, that i make a get request to an API, which returns true or false, and the resolve function do not wait for the get request to complete, so it just loads the state.
Here is my code:
app.run(['$rootScope', '$http', function($rootScope, $http) {
$rootScope.$on('$stateChangeStart', function() {
$http.get('/api/v1/getSetupStatus').success(function(res) {
$rootScope.setupdb = res.db_setup;
$rootScope.setupuser = res.user_setup;
});
});
}]);
app.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/404");
$stateProvider.state('db-install', {
url: "/install/db",
templateUrl: 'admin/js/partials/db-install.html',
controller: 'DBController',
resolve: {
data: function($q, $state, $timeout, $rootScope) {
var setupStatus = $rootScope.setupdb;
var deferred = $q.defer();
$timeout(function() {
if (setupStatus === true) {
$state.go('setup-done');
deferred.reject();
} else {
deferred.resolve();
}
});
return deferred.promise;
}
}
})
.state('user-registration', {
url: "/install/user-registration",
templateUrl: "admin/js/partials/user-registration.html",
controller: "RegisterController"
})
.state('setup-done', {
url: "/install/setup-done",
templateUrl: "admin/js/partials/setup-done.html"
})
.state('404', {
url: "/404",
templateUrl: "admin/js/partials/404.html"
});
}]);
Here you can see a timeline for the loading of the page:
Here you can see what the API returns:
Your db-install resolver function needs to chain from the $http.get for install status.
$stateProvider.state('db-install', {
url: "/install/db",
templateUrl: 'admin/js/partials/db-install.html',
controller: 'DBController',
resolve: {
data: function($state, $http) {
return $http.get('/api/v1/getSetupStatus'
).then (function(result) {
var setupdb = result.data.db_setup;
var user_setup = result.data.user_setup;
//return for chaining
return setupdb;
}).then (function (setupStatus) {
//use chained value
if (setupStatus === true {
//chain with $state.go promise
return $state.go('setup-done');
} else {
//resolve promise chain
return 'setup-not-done';
};
})
}
}
})
By returning and chaining from the status $http.get, the resolver function waits before executing (or not executing) the $state.go.
For more information on chaining promises, see the AngularJS $q Service API Reference -- chaining promises.
The call to getSetupStatus gets executed in the $stateChangeStart so resolve is not aware that it has to wait. You can put the $http call inside of the resolve function, like this:
function($q, $state, $timeout) {
return $http.get('/api/v1/getSetupStatus')
.then(function(res) {
if(res.db_setup) {
$state.go('setup-done');
}
else {
return true;
}
});
}
By making the resolve parameter return a callback the state will load after the promise is resolved.

angular injecting resolve into controller logging undefined

I have the following resolve:
.state('posts', {
url: '/posts/{id}',
templateUrl: 'posts.html',
controller: 'postsController as postsCtrl',
resolve: {
post: getSinglePostWrapper
}
})
and helper function
getSinglePostWrapper.$inject = ['postsService', '$stateParams'];
function getSinglePostWrapper(postsService, $stateParams) {
return postsService.getSinglePost($stateParams.id);
}
and my controller looks like this:
angular.module('flapperNews')
.controller('postsController', postsController);
postsController.$inject = ['postsService', 'post'];
function postsController(postsService, post) {
var vm = this;
vm.post = post;
console.log(post); //undefined here
}
I'm getting an undefined "post" when I try to inject the post from the resolve. I tried logging the post in the getSinglePostWrapper function, and it logs the correct object. I seem to be losing some binding or something from the resolve to the controller.
posts service
angular.module('flapperNews')
.factory('postsService', postsService);
postsService.$inject = ['httpService'];
function postsService(httpService) {
return {
getSinglePost: getSinglePost
}
function getSinglePost(postId) {
httpService.baseGet('/posts/' + postId)
.then(function(data) {
return data;
}, function(data) {});
}
}
httpservice
angular.module('httpService', [])
.factory('httpService', httpService);
httpService.$inject = ['$http', '$q'];
function httpService($http, $q) {
return {
baseGet: baseGet
}
function baseGet(url) {
return $http.get(url).then(
function (result) {
return result.data;
},
function (result) {
return $q.reject(result);
}
);
}
}
and I've injected httpservice into the first place I declare the flapperNews module.
FYI- everything is working. Other http requests are fine. This one is fine too. It just doesn't inject the post into the controller.
Promise chain breaks here.
function getSinglePost(postId) {
httpService.baseGet('/posts/' + postId)
.then(function(data) {
return data;
}, function(data) {});
}
You don't return the promise, hence post will be resolved to undefined before httpService.baseGet request has been finished.
try this:
.state('posts', {
url: '/posts/{id}',
templateUrl: 'posts.html',
controller: 'postsController as postsCtrl',
resolve: {
post: function('postsService', '$stateParams') {
return postsService.getSinglePost($stateParams.id);
}
})
angular.module('flapperNews')
.controller('postsController', function($scope, post){
$scope.post = post;
console.log($scope.post); //undefined here
});

AngularJS ui router $stateChangeStart with promise inifinite loop

I'm trying to build some sort of authentication in my angular app and would like to redirect to a external URL when a user is not logged in (based on a $http.get).
Somehow I end up in an infinite loop when the event.preventDefault() is the first line in the $stateChangeStart.
I've seen multiple issues with answers on stackoverflow, saying like "place the event.preventDefault() just before the state.go in the else". But then the controllers are fired and the page is already shown before the promise is returned.
Even when I put the event.preventDefault() in the else, something odd happens:
Going to the root URL, it automatically adds the /#/ after the URL and $stateChangeStart is fired multiple times.
app.js run part:
.run(['$rootScope', '$window', '$state', 'authentication', function ($rootScope, $window, $state, authentication) {
$rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
event.preventDefault();
authentication.identity()
.then(function (identity) {
if (!authentication.isAuthenticated()) {
$window.location.href = 'external URL';
return;
} else {
$state.go(toState, toParams);
}
});
});
}]);
authentication.factory.js identity() function:
function getIdentity() {
if (_identity) {
_authenticated = true;
deferred.resolve(_identity);
return deferred.promise;
}
return $http.get('URL')
.then(function (identity) {
_authenticated = true;
_identity = identity;
return _identity;
}, function () {
_authenticated = false;
});
}
EDIT: Added the states:
$stateProvider
.state('site', {
url: '',
abstract: true,
views: {
'feeds': {
templateUrl: 'partials/feeds.html',
controller: 'userFeedsController as userFeedsCtrl'
}
},
resolve: ['$window', 'authentication', function ($window, authentication) {
authentication.identity()
.then(function (identity) {
if (!authentication.isAuthenticated()) {
$window.location.href = 'external URL';
}
})
}]
})
.state('site.start', {
url: '/',
views: {
'container#': {
templateUrl: 'partials/start.html'
}
}
})
.state('site.itemList', {
url: '/feed/{feedId}',
views: {
'container#': {
templateUrl: 'partials/item-list.html',
controller: 'itemListController as itemListCtrl'
}
}
})
.state('site.itemDetails', {
url: '/items/{itemId}',
views: {
'container#': {
templateUrl: 'partials/item-details.html',
controller: 'itemsController as itemsCtrl'
}
}
})
}])
If you need more info, or more pieces of code from the app.js let me know !
$stateChangeStart will not wait for your promise to be resolved before exiting. The only way to make the state wait for a promise is to use resolve within the state's options.
.config(function($stateProvider) {
$stateProvider.state('home', {
url: '/',
resolve: {
auth: function($window, authentication) {
return authentication.identity().then(function (identity) {
if (!authentication.isAuthenticated()) {
$window.location.href = 'external URL';
}
});
}
}
});
});
By returning a promise from the function, ui-router won't initialize the state until that promise is resolved.
If you have other or children states that need to wait for this, you'll need to inject auth in.
From the wiki:
The resolve keys MUST be injected into the child states if you want to wait for the promises to be resolved before instantiating the children.

Return value in Javascript but it turns out to be undefined

I'm new at Javascripts and i'm trying to use Angular UI route, here is my code
myApp.config(['$stateProvider', '$urlRouterProvider',function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
url: '/test',
templateUrl: '/custom.html'
})
.state('detail', {
url: '/{examID}',
views: {
'': {
templateUrl: '/templates/customize.html',
controller: ['$scope', '$stateParams', 'utils',
function ( $scope,$stateParams,utils) {
$scope.exam = utils.findById($stateParams.examID);
console.log('exam is ' + $scope.exam );
}
]
}
}
} )
}])
and this is the service which has findbyID function
angular.module('service', [])
.factory('utils', function ( $http) {
return {
findById: function findById(id) {
$http.get('/api/exams/' + id).success(function(response) {
return response;
})}
};});
i've already follwed this topic but $scope.exam still undefined
How to return value from an asynchronous callback function?
PS. i've tried to print out response and it's an object
Thx
This is a place where a lot of developers new to JavaScript stumble.
What is going on here is that you are assigning the return value of utils.findById() to $scope.exam. The problem is that utils.findById() doesn't actually return anything. (When a function doesn't have an explicit return statement in JavaScript, the return value is implicitly undefined.)
Here is what your service should look like:
angular
.module('service', [])
.factory('utils', function ($http) {
return {
findById: function (id) {
return $http.get('/api/exams/' + id);
}
};
});
You probably noticed that the call to .success() has disappeared too! Don't worry. It just moved.
Instead of calling .success() on $http.get(), we want to call it on utils.findById(). Doing this will give you access to the response variable in your controller. Because you will have access to the response variable, you will be able to assign response to $scope.exam like so:
.state('detail', {
url: '/{examID}',
views: {
'': {
templateUrl: '/templates/customize.html',
controller: ['$scope', '$stateParams', 'utils',
function ($scope, $stateParams, utils) {
utils.findById($stateParams.examID)
.success(function (response) {
$scope.exam = response;
});
}
]
}
}
});
Hopefully that cleared it up. If I haven't been clear on anything, please let me know so I can update this answer.
You have to wait for the ajax call to finish. Modify the code in your controller to:
$scope.exam;
utils.findById($stateParams.examID).then(function(data) {
$scope.exam = data.data;
}
Read about the concept of 'Promises' in AngularJS and JavaScript.
Use deferred promise, So that it would return value after response
Service:
angular.module('service', [])
.factory('utils', function ( $http) {
return {
findById: function findById(id) {
var promise=$http.get('/api/exams/' + id);
return promise;
};});
Controller:
myApp.config(['$stateProvider',
'$urlRouterProvider',function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
url: '/test',
templateUrl: '/custom.html'
})
.state('detail', {
url: '/{examID}',
views: {
'': {
templateUrl: '/templates/customize.html',
controller: ['$scope', '$stateParams', 'utils',
function ($scope, $stateParams, utils) {
utils.findById($stateParams.examID).then(function(value) {
$scope.exam = value;
console.log('exam is ' + $scope.exam );
});
}
]
}
}
})
}])

Content doesn't update on route change

When I change route, from say /set/1 to /set/2, then it still shows the information from /set/1 until I manually refresh the page, I've tried adding $route.refresh to the ng-click of the links to these pages, but that didn't work either. Any ideas?
Below is the routing code, this works fine, all routing is done via links, just <a> tags that href to the route.
angular.module('magicApp', ['ngRoute']).config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/', {
templateUrl: 'pages/home.html'
}).when('/set', {
redirectTo: '/sets'
}).when('/set/:setID', {
controller: 'SetInformationController',
templateUrl: 'pages/set.html'
}).when('/card', {
redirectTo: '/cards'
}).when('/card/:cardID', {
controller: 'CardInformationController',
templateUrl: 'pages/card.html'
}).when('/sets', {
controller: 'SetListController',
templateUrl: 'pages/sets.html'
}).when('/cards', {
controller: 'CardListController',
templateUrl: 'pages/cards.html'
}).when('/search/:searchterm', {
controller: 'SearchController',
templateUrl: 'pages/search.html'
}).otherwise({
redirectTo: '/'
});
}]);
Below is the code for the SetListController, it uses the routeParams to grab the correct information from a service, which works, when I go to /set/1 then it returns the right information, if I then go back then go to /set/2 it still shows the information from set 1, until I refresh the page.
.controller('SetInformationController', function($scope, $routeParams, $route, SetInformationService, CardSetInformationService) {
$scope.set = [];
$scope.cards = [];
function init() {
SetInformationService.async($routeParams.setID).then(function(d) {
$scope.set = d;
});
CardSetInformationService.async($routeParams.setID).then(function(d) {
$scope.cards = d;
})
}
init();
})
The HTML itself has no reference to the controller, or anything like that, just the objects in the scope, namely set and cards.
I figured it out! The problem wasn't actually with the routing it was with my service, here was the service before:
.factory('SetInformationService', function($http) {
var promise;
var SetInformationService = {
async: function(id) {
if ( !promise ) {
// $http returns a promise, which has a then function, which also returns a promise
promise = $http.get('http://api.mtgdb.info/sets/' + id).then(function (response) {
// The then function here is an opportunity to modify the response
console.log("Set Information");
console.log(response);
// The return value gets picked up by the then in the controller.
return response.data;
});
}
// Return the promise to the controller
return promise;
}
};
return SetInformationService;
})
where it should have been:
.factory('SetInformationService', function($http) {
var promise;
var SetInformationService = {
async: function(id) {
// $http returns a promise, which has a then function, which also returns a promise
promise = $http.get('http://api.mtgdb.info/sets/' + id).then(function (response) {
// The then function here is an opportunity to modify the response
console.log("Set Information");
console.log(response);
// The return value gets picked up by the then in the controller.
return response.data;
});
// Return the promise to the controller
return promise;
}
};
return SetInformationService;
})

Categories

Resources