Here a sample codepen to explain my needs.
Basically I'm running a 2-pages Ionic application.
Page 1 is nothing but an hyperlink to page 2.
Page 2 reads data from an external source and requires some time (2 seconds).
I'd like to show a loading indicator while page 2 is "loading" that inhibits every user action on that page. Is there any good patterns to achieve this goal? I can probably use scope variables such as showPage and angularJs ng-show or something like that, but I would avoid to copy this pattern in every page.
Any idea?
You can use angular-block-ui.
It is very simple to implement in your project. Once you've referenced the css:
<link rel="stylesheet" href="path-to-block-ui/angular-block-ui.min.css"/>
and the javascript:
<script src="path-to-block-ui/angular-block-ui.min.js"></script>
you can add the dependency to your module:
angular.module('myApp', ['blockUI'])
It uses interceptors for long http calls. You can see how it works in the Block on HTTP requests in the examples page.
In your situation things are slightly different cause there's no http request there (I know, it's a demo) so you have to force the ui block manually.
You can change the service injecting the dependecy to blockUI:
.factory('MyService', function($q, blockUI) { ... })
and call blockUI.start(); right before the long running process and blockUI.stop(); when the promise is resolved.
.factory('MyService', function($q, blockUI) {
var items = ['1', '2', '3', '4', '5'];
return {
all: function() {
var deferred = $q.defer();
blockUI.start();
setTimeout(function() {
deferred.resolve(items);
blockUI.stop();
}, 2000);
return deferred.promise;
}
}
})
See your modified sample here.
In one of my apps I've used another angular module called angular-loading-bar. I guess it is the most popular these days. This one works with interceptors as well but it doesn't block the UI.
The third option is to use the ionic $ionicLoading service.
You have to do a little bit of extra work here as this service only show a loading indicator and blocks the interface.
You can create an interceptor:
var app = angular.module('ionicApp', ['ionic'])
app.config(function($httpProvider) {
$httpProvider.interceptors.push(function($rootScope) {
return {
request: function(config) {
$rootScope.$broadcast('loading:show')
return config
},
response: function(response) {
$rootScope.$broadcast('loading:hide')
return response
}
}
})
})
which listens to http requests and broadcasts an event loading:show.
When the server has responded with some data the interceptor broadcasts loading:hide.
Your app would intercept those events when the app runs:
app.run(function($rootScope, $ionicLoading) {
$rootScope.$on('loading:show', function() {
$ionicLoading.show({template: 'foo'})
})
$rootScope.$on('loading:hide', function() {
$ionicLoading.hide()
})
})
Answering my own question: $ionicLoading is what I was looking for.
Related
I am using AngularJS with ASP.NET MVC to create an application which uses Angular controllers for the front end.
When I run the application on my development system, it works fine. When I deploy it on my local IIS, it works fine. BUT, when I deploy it the to production server IIS, the angular controller does not load and it requires refreshing the page to get it to work.
I have looked at the similar questions, for instance:
Angular app only loads on page refresh
I also do not have any other extensions or plugins installed to affect the behavior as suggested in other similar questions.
I have moved all the JS in bundles, but that also to no avail. The order of the JS also seems to be correct because it works perfectly well on the development system, and local IIS.
I have no other idea on how to proceed with this issue.
Here's the screenshot for the error in the console:
And here's the code:
For HomeController,
app.controller('homeController', function ($scope, $uibModal, HomeService, RequestService) {
$scope.refreshOriginatorForms = function (searchCriteria) {
HomeService.getOriginatorRequestForms(searchCriteria).then(function (response) {
....
});
}
var originatorSearchCriteria = {
Pager: {
ItemsPerPage: 10,
CurrentPage: 1
}
};
$scope.OriginatorFormsSearchCriteria = originatorSearchCriteria;
$scope.initialize = function () {
$scope.refreshOriginatorForms($scope.OriginatorFormsSearchCriteria);
};
}
The $scope.initialize method is called in the view with ng-init="initialize()"
I have moved all the JS in bundles
If you are minimizing angular controllers then you should write our controller like this to that minimizers does not rename important angular keywords like $scope
app.controller('homeController',['$scope','$uibModal','HomeService','RequestService', function ($scope, $uibModal, HomeService, RequestService) {
$scope.refreshOriginatorForms = function (searchCriteria) {
HomeService.getOriginatorRequestForms(searchCriteria).then(function (response) {
....
});
}
var originatorSearchCriteria = {
Pager: {
ItemsPerPage: 10,
CurrentPage: 1
}
};
$scope.OriginatorFormsSearchCriteria = originatorSearchCriteria;
$scope.initialize = function () {
$scope.refreshOriginatorForms($scope.OriginatorFormsSearchCriteria);
};
}])
We finally got it solved. Our network engineer suggested enabling the CDN on the DNS, and it worked.
All this time, looking at the code, and the issue was something else.
I am a backend API developer, new to AngularJS (version 1), which is what my current assignment is written in.
The page has ng-app="app" found in the <html> tag, and ng-controller="BaseController as baseCtrl", and renders just fine.
From here, there is a block of (currently server-side) content that needs to instead be called by ajax, post-render, because its source is unreliable and sometimes slow. I've already written the API URL to return the content as e.g.
{"status":"OK","id":"feed-whatsnew","content":"Unreliable source!"}
My question is, how do I use Angular to populate the "content" above into the content portion of <div id="feed-whatsnew"> .. <div class="content"></div>..</div> position, and also unhide feed-whatsnew?
So far, angular rendering makes sense to me but any secondary process like this is still opaque. I know how I would have handled it in jQuery but want to avoid trying to simply "mimic" a library vs. truly implementing the Angular framework.
In angular you could use the $http-provider to call an external API using ajax. Here's an example of how you would do that
app.controller('BaseController',['$http', function($http){
var vm = this;
vm.content = {};
vm.fetchContent = function(){
return $http.get('/my-api-endpoint').then(function(response){
vm.content = response.data;
}, function(response){
console.error('Failed calling /my-api-endpoint ', response);
});
};
// Initiallly load content
vm.fetchContent();
}]);
The call to $http.get(..) start's an asynchronous call to your given external url and returns something called a Promise. When the asynchronous call finishes, this Promise resolves, which executes the callback passed to the Promise using then([callback]). This callback then populates your vm.content variable in your controller, with the data passed in the response.
You can read more about the $http provider in the angular documentation
When you want to render your content somewhere, you would do something like this:
<span>{{baseCtrl.content}}</span>
Although that part depends highly on what content you're presenting.
You should probably separate the fetchContent() function into an Angular service, but that's an entirely different topic.
AngularJS shipped with powerful $http library to perform the ajax. Please go through relatively easy documentation available at https://www.w3schools.com/angular/angular_http.asp
Here are the logical steps
Inject $http in controller dependency
Make service call through $http
On Success, assign the response to the $scope variable to make
it available to UI/html markup. Assignment will differ if you are using controller as in the markup.
angular.module('app', [])
.controller('BaseController', function($http) {
var vm = this;
vm.feed = null;
_getDataFromServer();
function _getDataFromServer() {
var req = {
url: 'https://jsonplaceholder.typicode.com/posts/1',
method: 'GET' //could be post/put etc
//data: someJson -- if webservice need so
};
$http(req)
.then(function(response) {
vm.feed = response.data;
}).catch(function(err) {
console.log('some error occured!', err);
});
}
});
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular.min.js"></script>
<div ng-app="app" ng-controller="BaseController as vm">
<p>Using $http</p>
<div id="feed-whatsnew" ng-hide="!vm.feed">
<!-- use {{}} or ng-bind to bind the data -->
<!-- use ng-hide to show/hide the content conditionally -->
Title from Ajax Response: {{vm.feed.title}}
<div class="content">
Content:
<p ng-bind="vm.feed.body"></p>
</div>
</div>
</div>
I have a few controllers that call the method getData() from a service.
In order to not do extra http calls for the same json file, i'm using something like this:
QuizApp.service('quizService', ['$http', function($http) {
var quizService = {},
quizData;
quizService.getData = function() {
return quizData ? quizData : quizData = $http.get('quiz.json');
};
return quizService;
}]);
...but things don't work properly if I do it like that (the data is used to populate a slider and a thumbnail gallery with angular-slick and some problems arise. For now it maybe doesn't matter, I just want to know if the code above makes sense).
On the other hand, if I write getData() like this:
QuizApp.service('quizService', ['$http', function($http) {
var quizService = {},
quizData;
quizService.getData = function() {
quizData = $http.get('quiz.json');
return quizData;
};
return quizService;
}]);
... which will do various http requests for the same json file (doesn't look like a good practice to me), everything works fine and the slick angular gallery works properly. But not 100% of the times though: kind of randomly things don't work well too (same symptoms. I might describe them but again, I don't think that's the point here)
So, in general, regardless of the context, which one of those versions of getData() looks good and which doesn't and why?
UPDATE
As Mark pointed out, Angular has a built in cache, but it's set to false by default. Here is a post and here is the documentation.
If I cache the result of the http request though I get the same problem (I'm not describing it here) I was getting with my second option, and it has apparently nothing to do with that.
Actually, it seems that if I repeat the http request two times (as in my second snippet of code) things work by chance (90% of the time?).
So, by caching the result, at least I get a consistent result, which means in this case that the slick-angular thing won't work properly (never) and I have to look for a solution somewhere else.
Angular $http has a built in cache, which you could make use of here. You can cache all $http requests, which is probably a bad idea, or specific ones.
On a very simple level, this should work for you
quizService.getData = function() {
return $http.get('quiz.json', {cache: true}).then(quizData => {
return quizData;
});
};
You can find out more in the Angular docs for $http
I have an site I have put together based on a tutorial I found here
http://www.revillweb.com/tutorials/angularjs-in-30-minutes-angularjs-tutorial/
My app.js contains
angular.module('kolvir', ['ngRoute'])
.controller("kolvirController", function($http){
var vm = this;
vm.searchInput = '';
$http.jsonp (
'https://sfcons.azure-mobile.net/api/getcons/?
callback=JSON_CALLBACK')
.success(function (data){
vm.cons = data;
})
.error(function (data) {
console.log('ERROR');
});
});
I added a controller (gallery-controller.js )and routing appears to fail. After ripping things out I determined that adding the module call causes the failure, with:
angular.module('kolvir', ['ngRoute']);
Without it works just fine. I have a second site based on twitter bootstrap that uses the same pattern and it works just fine.
I am still new enough to angular I am not even certain what I should be searching for to find and answer, but so far I have not turned up anything similar to this.
It's kind of tricky how to load js files in an Angular project together with Google Client js api.
This question is talking about the correct order of doing this.
Angular Js and google api client.js (gapi)
And there is an Official Doc talking about this,
https://cloud.google.com/developers/articles/angularjs-cloud-endpoints-recipe-for-building-modern-web-applications/.
One thing in the doc is that it uses window.init() inside of init which will be causing an infinite loop.
As willlma pointed out, we should use a different name for the function.
But I have met an error of
Uncaught TypeError: window.initGAPI is not a function
The project is created by using yeoman generator for angular.
The order of loading js in index.html
<script src="scripts/app.js"></script>
<script src="scripts/controllers/main.js"></script>
<script src="scripts/controllers/about.js"></script>
<script src="https://apis.google.com/js/client.js?onload=init"></script>
app.js(only the latter part of this file):
var init = function(){
console.log("gapi init");
window.initGAPI();
}
main.js:
angular.module('testApp')
.controller('MainCtrl', function ($scope, $window) {
$window.initGAPI = function(){
console.log("controller inti");
}
$scope.awesomeThings = [
'HTML5 Boilerplate',
'AngularJS',
'Karma'
];
});
I have put some logs to test and found that the definition for $window.initGAPI is executed before loading the cliet.js which calls window.init, but inside of window.init, window.initGAPI is not defined. It seems in the main controller, defining a function called initGAPI to object window failed.
I think is not so easy to correctly load both libraries each in its way, because both are asynchronous in a different way.
Instead, is easier to let one load the other.
The first and simplier approach is to put the client.js lib at the end of all scripts, and then do a manual Angular bootstrap when the Google library is fully loaded.
Other method could be the opposite, so let Angular create the Google client.js script tag and load the library.
Check this project: https://github.com/canemacchina/angular-google-client
I've create this library to help me using Google client library in Angular, and it use the second method...
I had a similar issue. Perhaps you can try something like this for the init function so that it retries again when the controller is not loaded yet.
function init() {
if (window.initGapi != undefined) {
window.initGapi();
}
else {
setTimeout(init, 500); // wait for 500 ms
}
}