I'm currently working on a project in angular. I'm trying to show a loading spinner every time the page changes. To accomplish this we are using ngRoute module and listening for routeChangeStart,routeChangeSuccess,routeChangeError events.
Here's the code:
$scope.$on('$routeChangeStart', function (event, param) {
var html = "<div class='panel-body'>"
+ "<div class='col-md-4 col-md-offset-4' style='top: 50%;'>"
+ "<i class='fa fa-spinner fa-spin'></i> Caricamento in corso..."
+ "</div>"
+ "</div>";
that.myModal = $modal.open({
template: html,
backdrop: 'static'
});
});
$scope.$on('$routeChangeSuccess', function (event, param) {
that.myModal.dismiss('nessuna');
});
$scope.$on('$routeChangeError', function (event, param) {
that.myModal.dismiss('nessuna');
});
This works, but only the first time a certain page is changed. I try to explain better: When we are in page X and navigate to page Y the modal is shown and then hidden after page changes. If then i go back to page X and navigate to page Y again the modal spinner is now shown.
When debugging I can see the modal.open() executing, but it's never shown. It looks like angular is somehow delaying the command.
Does anyone know why is this happening? Has anyone encountered this problem before?
About displaying the spinner solution capturing $http requests, here you have some info to get started:
http://lemoncode.net/2013/07/31/angularjs-found-great-solution-to-display-ajax-spinner-loading-widget/
Showing Spinner GIF during $http request in angular
The idea is to intercept a $http request and display the spinner, then on the end request or promise response (error and success) check if there are pending requests, if not hide the spinner.
If you need more info, give me a buzz, I have further developed this and taken some solutions for issues like I don't want to show the spinner for some particular requests.
Whatever solution you keep to catch route change events, I don't understood why your modal doesn't trigger.
Here is a demo in plunker (I used ui-bootstrap to handle modal).
You can use this nice script too called Pace to automatically start a loader based on http request.
Taken from their documentation:
Pace will automatically monitor your ajax requests, event loop lag, document ready state, and elements on your page to decide the progress. On ajax navigation it will begin again!
Related
Right now I am using angular.element(document).ready(init()); but it calls init(); on every page refresh and not when browsing back and forth to the page. How could I call this function on every page view ?
I've tried also onload and ng-init and they don't work - the function doesn't get called.
I think this is what you're looking for:
$routeChangeSuccess Broadcasted after a route change has happened successfully. The resolve dependencies are now available in the
current.locals property.
ngView listens for the directive to instantiate the controller and
render the view.
This is what I do and it works for me:
$scope.$on('$routeChangeSuccess', function () {
// do something
});
Unless you're using ui-router. Then it's:
$scope.$on('$stateChangeSuccess', function () {
// do something
});
More info is found in the docs
I have searched on StackOverflow for the last five hours and none of the related answers quite solve my problem. I have an UI-Router state that loads a long list of messages generated from a custom directive. This page is linked too in many places pointing to a different message. I want to scroll to the currently selected message.
I can get this to work using $anchorScroll if I surround the call with a $timeout. $timeout(function(){$anchorScroll()};) but if the $timeout is not there a call to $anchorScroll does nothing since the View has not completely loaded.
Here is most of the relevant code.
<message id='message{{message.id}}'
ng-repeat='message in messages'
message='message'
ng-class="{'current':current == 'message{{message.id}}'}" >
</message>
In the controller I set current to $scope.current = $location.hash(). This all works.
If I load the page like #/messages#message100 directly the page will correctly scroll. However, if from a different view I use the a link such as this:
<button ui-sref="message-state({'#':'message{{message.id}}'})>
Go To Message {{message.id}}
</button>
The page will not automatically scroll to the correct anchor since the message list has not been made yet. But by putting the call to $anchorScroll() in a $timeout I can make the page scroll.
I don't like using $timeout for this purpose. I know I am not supposed to manipulate the DOM in a controller like this.
I have tried registering the call to $anchorScroll() with many of the $stateProvider events such as:
$state.$on('$viewContentLoaded', function(event) {
$anchorScroll();
});
But even at the time the $viewContentLoaded fires the message list does not exist in the DOM and the page does not scroll.
IWhat is the best way to make the UI-Router scroll based on the $location.hash().
Even I was facing a similar situation and after days of try and error, I came up with this.
In ui router add an id parameter to the state on which you want to enable the scroll.
$stateProvider.state('index', {
url: '/',
params: {
id: null
}
})
Then in the html use ui-sref
<li><a ui-sref="index({id: 'about-us'})" >About Us</a></li>
At last in the app.run module detect the state change using
$rootScope.$on('$viewContentLoaded', function(event){
if ($state.current.name == 'index') {
if($stateParams.id) {
$anchorScroll.yOffset = 150;
$location.hash($stateParams.id);
$timeout(function(){$anchorScroll()}, 1000);
}
}
});
Hope this helps. Would do a plunkr if needed.
I am using angularjs for my app. It contains a newsfeed. If user needs to view the particular news, he has to click on that particular news and it will redirect to that news. And after viewing the particular news, if user wants to go back to news feed(previous page), he has to click on the browser back button. So, in this case, if user clicks on the browser back button, the newsfeed page is reloading again. But, I dont want like this. I want to disable the reload and take the user to the place where he was before. I browsed a lot on this issue, but there was no absolute solution given. Please, help me out.
When you go back, previous route will be triggered and data will be reloaded. If you want to prevent reloading, place a service in between and cache the data.
$routeProvider.
when('/loadFeeds',{
controller:'LoadFeedCtrl',
templateUrl:'pages/feeds.html',
resolve:{
initData : function(FeedService){
return $q.all(
{
data: FeedService.load()
}
);
}
}
})
And in your FeedService, instead of making http call try to see the data is already available
.service('FeedService',function($q,$timeoute){
this.feeds=[];
this.load = function(){
var deferred = $q.defer();
$timeout(function(){
if(feeds.length===0){
this.feeds.push({title:'First','id':1})
this.feeds.push({title:'Second','id':2})
this.feeds.push({title:'Third','id':3})
this.feeds.push({title:'Fourth','id':4})
}
deferred.resolve(this.feeds);
},2000);
return deferred.promise;
};
})
The timeout service could be replaced with $http or $resource. I used the timeout to simulate the delay
you can take an anchor link and on click event of anchor call this
javascript function
window.window.history.back();
here is the html code for it
<input type="button" id="alnkBack" onclick="window.window.history.back();" value="Back" class="button" />
I am trying to show the modal dialog while my page is being loaded. Right now I show the modal before the page push (e.g. app.navi.pushPage( 'detail.html' ) ). In the page init i have to go out and get some data from a third party API, and display it in a list. Once the request is complete and the list is populated I hide the modal.
The issue is that the modal is actually being hidden before the transition starts. Any ideas on how I can hide the modal once the transition is complete and the DOM is loaded?
Thanks!
Here there is an example that hides the modal after 2 seconds: http://onsen.io/guide/overview.html#UsingModal
In your case I guess you are using an HTTP request or something similar to access the third party API, so it's necessary to hide the modal after preparing all the data in the callback of the request:
$http.get('/third/party/API').
success(function(data, status, headers, config) {
// this callback will be called asynchronously
// when the response is available
myItemList = data[...];
... // refresh view, pushPage or whatever you need to prepare
modal.hide();
}).
error(function(data, status, headers, config) {
// Handle errors in request
});
Hope it helps!
When switching Views in AngularJS (from #1 to #2), I am sending two XHR requests to the server. One of them is finished quicker and as soon as it is, the template gets rendered. However, in the template I'm referring to data that comes back from the second request, which at that time is not finished yet.
Is there a way that I can wait for all requests to finish before rendering the template?
Currently I'm simply defining methods in the controller and then at its bottom, executing the XHR requests and assigning the response to $scope variables.
If you're using the $compile directive to render your HTML-Templates dynamically, you could add
ng-show="showCtrl"
And in your controller preset
$scope.showCtrl = false;
If you have the standard route provider, wrap your HTML-Template with a DIV e.g.
<div style="display:none" or ng-show="showCtrl">
If your XHR Request is finished, just take the DIV-Element with attribute and say
display:block or
showCtrl = true; $scope.$apply();
Greetings
You should be using the promise service
Please refer to
https://docs.angularjs.org/api/ng/service/$q
I'd say the best way to handler this is to use a resolve on your router.
If you are using the default Angular router, see the docs here: https://docs.angularjs.org/api/ngRoute/provider/$routeProvider, specifically the resolve property of the route definition. It allows you to specify any async calls that will be completed before the route changes and the next view is loaded.
If you use angular-ui-router, there is the exact same concept there.
Here is an example of using resolve from the angular docs:
.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;
}
}
});
});
Obviously they have a fake async call here with a timeout, but as long as your XHR call returns a promise (which will be the case if you use the $http service), then your controller can just be injected with delay in this case, and use the resolved data straight away.