How to make ng-view wait for XHR response? - javascript

I´m trying to make the ng-view wait for a xhr request. I have two controllers for a routed ng-view, the first one is loaded perfectly. But the other doesn't gets rendered well, because the xhr response happens after partial.html is downloaded. How do I avoid the partial.html request until that client get the xhr response?
You can see below the code for the route configuration:
var configuration = [
'$routeProvider',
'$locationProvider',
function(routeProvider, locationProvider) {
routeProvider.when('/', {
templateUrl: '/partials/hotelinfo.html',
controller: 'HotelInfo'
}).when('/service/dept/:id', {
templateUrl: '/partials/department.html',
controller: 'Department'
}).otherwise({
redirectTo: '/'
});
locationProvider.html5Mode(true);
}
];
Below you can see the controller configuration that gets the xhr response
<!-- language: lang-js -->
var Department = [
'$scope',
'$routeParams',
function (scope, routeParams) {
http.get('/service/dept/' + routParams.id).success(function (data) {
scope.data = data;
});
}
];

Instead of calling $http.get from your controller, call it from a resolve function on $routeProvider and inject it into the controller. That will cause Angular to not load your view until the promise from $http is resolved.

You can accomplish this using resolve in the routeProvider. It returns a promise. The view will not load until that promise is resolved. You can resolve that promise in your controller.
See http://docs.angularjs.org/api/ngRoute/provider/$routeProvider for more info.
var configuration = [
'$routeProvider',
'$locationProvider',
function(routeProvider, locationProvider) {
routeProvider.when('/', {
templateUrl: '/partials/hotelinfo.html',
controller: 'HotelInfo'
}).when('/service/dept/:id', {
template: '/partials/department.html',
controller: 'Department',
resolve: {
deferred: function($q) {
return $q.defer();
}
}
}).otherwise({
redirectTo: '/'
});
locationProvider.html5Mode(true);
}
];
var Department = [
'$scope',
'$routeParams',
'deferred',
function (scope, routeParams, deferred) {
http.get('/service/dept/' + routParams.id).success(function (data) {
scope.data = data;
deferred.resolve();
});
}
];

Related

angular resolve reload when same route reloading

How can I reload the Angular resolve with the reload?
My code :
.when('/dashbord', {
title: 'dashbord',
templateUrl: 'views/dashbord.php',
controller: 'dashbordController',
resolve: {
getDashbord: function (getDashbordService) {
return getDashbordService;
}
}
})
Reload function :
app.run(['$rootScope', '$route', '$templateCache', function ($rootScope, $route, $templateCache) {
$rootScope.changeRoute = function(){
var currentPageTemplate = $route.current.templateUrl;
$templateCache.remove(currentPageTemplate);
$route.reload();
};
}]);
The above function reload only the view. It doesn't reload the resolve.
How can i do it in angular ?
I created plunker for you that shows that
$route.reload()
fires resolve on state
I know this is a late answer, but one thing to keep in mind is that Angular services are singletons -- which means if you define your resolve function as a service (esp. one that makes an $http request), you will get the same results each time the route resolves.
For example, this uses an Angular service that makes an $http request, and won't make a new $http request on $route.reload():
angular.module('myApp').factory('getDashboardService', ['$http', function getDashboardService($http) {
return $http.get('/path/to/resource'); // returns a promise
}]).config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/dashboard', {
title: 'dashboard',
templateUrl: 'views/dashboard.php',
controller: 'DashboardController',
resolve: {
getDashbord: 'getDashboardService' // refers to an Angular service
}
});
}]);
...but this will, because it is not an injected service:
angular.module('myApp').config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/dashboard', {
title: 'dashboard',
templateUrl: 'views/dashboard.php',
controller: 'DashboardController',
resolve: {
getDashboard: ['$http', function($http) {
return $http.get('/path/to/resource');
}]
}
});
});
Here is a JSFiddle: https://jsfiddle.net/sscovil/77ys9hz5/

Angular load route when ajax returns

How can I delay/defer a route/controller until an anonymous function returns? On app bootstrap, I default rootScope.me as guest account until it can check cookies for a logged in user.
I have a controller, testCtrl, that relies on rootScope.me data to load appropriate user data. The controller is fired before rootScope.me has a chance to be set to the user.
I know Angular has $q service for resolving promises, but am not sure how to apply this to routing.
angular
.module('DDE', [])
.run(['$rootScope', 'Me', function($rootScope, Me) {
$rootScope.me = {
username : 'Guest',
id : -1
};
if (Cookies.get('user_id') && Cookies.get('username')) {
Me.getProfile({user_id : Cookies.get('user_id')}).success(function (res) {
$rootScope.me = res;
}).error(function (err) {
console.log('Error: ', err);
});
}
}])
.config(['$routeProvider', '$httpProvider', '$authProvider',
$routeProvider.
when('/test', {
templateUrl: '/html/pages/test.html',
controller: 'testCtrl'
}).
.config(['$routeProvider', '$httpProvider', '$authProvider', '$stateProvider', '$urlRouterProvider',
function($routeProvider, $httpProvider, $authProvider, $stateProvider, $urlRouterProvider) {
//Cannot inject services like Me or $rootScope as I need
function loadProfile () {
Me.getProfile({user_id : Cookies.get('user_id')}).success(function (res) {
$rootScope.me = res;
}).error(function (err) {
console.log('Error: ', err);
});
}
$stateProvider.
state('test', {
url : '/test',
templateUrl: '/html/pages/test.html',
controller : 'testCtrl',
resolve : {
ProfileLoaded : function () {
return loadProfile();
}
}
});
edit: adding angular's ngRoute example.
You can look into ui-router's resolve. It basically waits for your promise to be resolved before loading/navigating to your state/route.
documentation
Each of the objects in resolve below must be resolved (via
deferred.resolve() if they are a promise) before the controller is
instantiated. Notice how each resolve object is injected as a
parameter into the controller.
Here's angular's ngRoute example from angular's documentation:
.config(function($routeProvider, $locationProvider) {
$routeProvider
.when('/Book/:bookId', {
templateUrl: 'book.html',
controller: 'BookController',
resolve: {
// I will cause a 1 second delay
delay: function($q, $timeout) {
var delay = $q.defer();
$timeout(delay.resolve, 1000);
return delay.promise;
}
}
})

Creating a promise within resolve in routes for Angular app

I am currently working on an Angular app, but I am having difficulty implementing a promise with resolve. What I want to accomplish is in the following:
Get a users geolocation
Use the users geolocation as parameters for an API call to SongKick
After the data has been received from the API call successfully I want the home.html page to load with the data found in q.resolve
All want all of this to happen in order. Essentially, there is data I need to obtain before displaying my home page. The problem is that when I console log getLocation in my homeCtrl it is undefined. Anyone know why or have a better way to approach this kind of thing?
FYI:assignValues is a success callback after geolocation values have been defined.
routes.js
angular.module('APP', ['ui.router',
'APP.home',
'uiGmapgoogle-maps'
])
.config(function($urlRouterProvider, $stateProvider, uiGmapGoogleMapApiProvider) {
$stateProvider.state("home", {
url:"/",
templateUrl: '/home.html',
controller: 'homeCtrl',
resolve: {
getLocation: function(dataFactory, $q){
var q = $q.defer();
navigator.geolocation.getCurrentPosition(assignValues);
function assignValues(position) {
dataFactory.getMetroArea(position.coords.latitude, position.coords.longitude).then(function(data){
q.resolve(data);
return q.promise;
})
}
}
}
})
HomeCtrl.js
angular.module('APP.home',['APP.factory'])
.controller('homeCtrl', ['$rootScope', '$scope', '$http', '$location', 'dataFactory', 'artists','uiGmapGoogleMapApi', 'getLocation', homeCtrl])
function homeCtrl($rootScope, $scope, $http, $location, dataFactory, artists, uiGmapGoogleMapApi, getLocation){
$scope.googleMapsData = getLocation
}
dataFactory.js(left out rest of factory)
dataFactory.getMetroArea = function(lat, lon){
return $http.get('http://api.songkick.com/api/3.0/search/locations.json?location=geo:'+ lat + ',' + lon + '&apikey=APIKEY')
}
Resolve methods need to either return a promise, or actual data. Here's a cleaned up resolve method which include rejections (you don't want to leave your request hanging).
angular.module('APP', ['ui.router', 'APP.home', 'uiGmapgoogle-maps'])
.config(function($urlRouterProvider, $stateProvider, uiGmapGoogleMapApiProvider) {
$stateProvider.state("home", {
url: "/",
templateUrl: '/home.html',
controller: 'homeCtrl',
resolve: {
getLocation: function(dataFactory,$q) {
var q = $q.defer();
navigator.geolocation.getCurrentPosition(function(position){
dataFactory.getMetroArea(position.coords.latitude, position.coords.longitude).then(function(data){
q.resolve(data);
},function(err){
q.reject(err);
})
},function(err){
q.reject(err);
});
return q.promise;
}
}
});
});
I think your getLocation function should be
getLocation: function(dataFactory, $q){
var q = $q.defer();
navigator.geolocation.getCurrentPosition(assignValues);
function assignValues(position) {
dataFactory.getMetroArea(position.coords.latitude, position.coords.longitude)
.then(function(data){
q.resolve(data);
});
}
return q.promise;
}

how do I set up my angularjs controller

I'm just messing around with angular a bit and I built a simple task API. This api has assigned and accepted tasks. Now when building the app I have these routes:
TaskManager.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/:username/assigned-tasks', {
templateUrl: 'app/partials/assigned-tasks.html',
controller: 'TaskController'
}).
when('/:username/accepted-tasks', {
templateUrl: 'app/partials/assigned-tasks.html',
controller: 'TaskController'
}).
otherwise({
redirectTo: '/'
});
}]);
And here is the task controller I started building and then realized this was not going to work
TaskManager.controller('TaskController', ['$scope', 'AssignedTasksService', function($scope, AssignedTasksService)
{
$scope.tasks = [];
loadAssignedTasks();
function applyRemoteData( Tasks ) {
$scope.tasks = Tasks;
}
function loadAssignedTasks() {
AssignedTasksService.getAssignedTasks()
.then(
function( tasks ) {
applyRemoteData( tasks );
}
);
}
}]);
The getAssignedTasks funciton is just a function that runs a http get request to the api url and either returns and error or the api data
now as you can see the assigned tasks are automatically loaded once it hits the TaskController which is obviously a problem since I need to also be able to get accepted tasks. Now do I need to create a separate controller for accepted tasks or is there a way for maybe me to check the url from the controller and from there I can decide if I want to run the loadAssignedTasks function or the loadAcceptedTasks (which I haven't created yet). but it would just do the same thing as the loadAssignedTasks function but for the accepted tasks
As mentioned in the comments there are multiple ways to solve. All depending on current use case. But you should probably use seperate controllers to solve this problem. Also inject the data(tasks) into the controller rather than fetching them inside the controller. Consider the following example:
var resolveAssignedTasks = function(AssignedTasksService) {
return AssignedTasksService.getAssignedTasks();
};
var resolveAcceptedTasks = function(AcceptedTasksService) {
return AcceptedTasksService.getAcceptedTasks();
};
TaskManager.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/:username/assigned-tasks', {
templateUrl: 'app/partials/assigned-tasks.html',
controller: 'TaskController',
resolve: {
tasks: resolveAssignedTasks
}
}).
when('/:username/accepted-tasks', {
templateUrl: 'app/partials/assigned-tasks.html',
controller: 'TaskController',
resolve: {
tasks: resolveAssignedTasks
}
}).
otherwise({
redirectTo: '/'
});
}]);
Controllers:
TaskManager.controller('AssignedTaskController', ['$scope', 'tasks', function($scope, tasks)
{
$scope.tasks = tasks;
}]);
TaskManager.controller('AcceptedTaskController', ['$scope', 'tasks', function($scope, tasks)
{
$scope.tasks = tasks;
}]);
You could also by doing this use a single controller by merging the resolveFunctions into one function that returns the appropriate tasks depending on the current route. Hope this helps.

How do I prevent a route from changing before resolve has completed?

How can I make angular wait until my service has finished loading before changing the route? It was my understanding that using 'resolve' in $routeProvider would accomplish this, but it is not working for me. I'm using Angular v1.2.1.
Here's how my router is setup:
angular.module('myApp', [
'ngRoute'
])
.config(function ($routeProvider) {
$routeProvider
.when('/myRoute', {
templateUrl: 'myTemplate.html',
controller: 'MyCtrl',
resolve: {
MyVar: function(MyService) {
return MyService.query();
}
}
})
...
And here is my service:
angular.module('myApp')
.factory('MyService', function MyService($resource) {
return $resource("/api/example/:id",
{
id: "#id"
},
{
query: {
method: "GET"
},
get: {
method: "GET"
},
update: {
method: "PUT"
},
delete: {
method: "DELETE"
}
});
});
ngResource methods do not return a promise but a special resource object which doesn't wait for the request to complete. Thus, your route changes before the service has finished loading.
So, when using ngResource you need to manually create, resolve, and return a promise using $q service:
...
resolve: {
MyVar: function(MyService, $q) {
var deferred = $q.defer();
return MyService.query(function(results){
deferred.resolve(results);
});
return deferred.promise;
}
}

Categories

Resources