Hi I am trying to create a laoding box in my angular application here is what I have so far:
This is the loadingBox directive:
app.directive('loadingBoxDir', function (eventsSvc) {
var loadingBoxDir = {
templateUrl: '/Scripts/App/Infrastructure/Directives/loadingBoxView.html',
restrict: "E",
scope: {
isLoadingBoxVisible: '='
}
};
loadingBoxDir.link = function ($scope, $element) {
var loadingBox = $element.find('.fa-spinner');
var body = angular.element('body');
var loadingBoxParent = $element.find('#global-spinner');
loadingBox.css({
"position": "absolute",
"top": body.height() / 2 - loadingBox.height() / 2 + "px",
"left": body.width() / 2 - loadingBox.width() / 2 + "px"
});
$scope.$on(eventsSvc.global.loadingBoxShow, function () {
loadingBoxParent.addClass('global-spinner-visible');
});
$scope.$on(eventsSvc.global.loadingBoxHide, function() {
loadingBoxParent.removeClass('global-spinner-visible');
});
}
return loadingBoxDir;
});
And this is my interceptor:
app.provider('httpInterceptorSvc', [function () {
this.$get = function ($q, $rootScope, $injector, eventsSvc) {
var httpInterceptorSvc = {};
httpInterceptorSvc.request = function (requestParam) {
$rootScope.$broadcast(eventsSvc.global.loadingBoxShow);
return requestParam;
}
httpInterceptorSvc.response = function (responseParam) {
hideLoadingBox();
return responseParam;
}
httpInterceptorSvc.requestError = function (requestErrorParam) {
hideLoadingBox();
return $q.reject(requestErrorParam);
}
httpInterceptorSvc.responseError = function (responseErrorParam) {
hideLoadingBox();
return $q.reject(responseErrorParam);
}
function hideLoadingBox() {
var $http = $injector.get('$http');
if ($http.pendingRequests.length === 0) {
$rootScope.$broadcast(eventsSvc.global.loadingBoxHide);
}
}
return httpInterceptorSvc;
}
}])
This is the events service:
app.factory('eventsSvc', [function () {
var eventsSvc = {
global: {
loadingBoxHide: 'global.loadingBox.hide',
loadingBoxShow: 'global.loadingBox.show'
}
};
return eventsSvc;
}])
Now as you can see in the interceptor in both the request and response I am trying to broadcast to the loadingBoxDir. The problem is that the request broadcast does not reach the directive.
One interesting fact is that the response broadcast reaches the directive and I cannot figure out what is the difference between the two
Can anyone tell me what I am doing wrong?
I figured out the problem with the code.The ideea is that ajax calls get executed as soon as the page loads, but the directives did not have time to be initialized that's why when broadcast gets called it does not reach this on function:
$scope.$on(eventsSvc.global.loadingBoxShow, function () {
loadingBoxParent.addClass('global-spinner-visible');
});
In order to solve the problem I added this piece of code:
loadingBoxParent.addClass('global-spinner-visible');
What this does is sets the loading box visible as soon as the page loads which makes sense because I am executing ajax calls in the controller.
Related
How can I check if I have to stop calling the loadMore() function, because all the documents have already been loaded from the database?
In the example below I'm using Ionic, but it's the same also with ng-infinite-scroll in AngularJS apps.
This is my actual code:
HTML:
...
<ion-infinite-scroll
ng-if="!noMoreItemsToLoad"
on-infinite="loadMore()"
distance="5%">
</ion-infinite-scroll>
</ion-content>
JS Controller:
$scope.loadMore = function(){
console.log('Loading more docs...');
Items.loadMore(); // calling the .next() method inside the Items service
if( !Items.hasNext()) { $scope.noMoreItemsToLoad = true; }
$scope.$broadcast('scroll.infiniteScrollComplete');
}
JS Items Factory:
.factory('Items', function (FIREBASE_URL, $firebaseArray, $firebaseObject) {
var itemsRef = new Firebase(FIREBASE_URL + 'items/');
var scrollRef = new Firebase.util.Scroll(itemsRef, 'name');
var self = {
getAllItems : function(){ ... },
loadMore: function(){
scrollRef.scroll.next(4);
},
hasNext: function(){
if(scrollRef.scroll.hasNext()) { return true; }
else { return false; }
}
}
return self;
}
Do the scroll.next in timeout, for example:
loadMore: function(){
$timeout(function() {
scrollRef.scroll.next(4);
});
},
I had the same issue and I think the solution is to modify the hasNext() function on firebase.util.js:
Cache.prototype.hasNext = function() {
return this.count === -1 || this.endCount >= this.start + this.count;
};
I put a missing equal sign (=) before this.start
I hope it works for you.
i'm trying to display a Bootstrap modal Dialog when a Variable in a Service is changed.
I'm currently doing this with broadcast. I'm new to angular so any help to improve my code is appreciated.
This Code works but i wanted to know if this is the correct way to do this.
My Service:
utils.service('UtilsService', function ($rootScope) {
this.objectToDelete = "";
this.deleteMessage = "";
this.DeleteObject = function (object, message) {
this.objectToDelete = object;
this.deleteMessage = message;
$rootScope.$broadcast('objectChanged', this.objectToDelete);
}
});
My Directive:
verwaltungApp.directive('deletedialog', ['UtilsService', function (UtilsService) {
return {
restrict: 'E',
controller: function ($scope, $attrs, UtilsService) {
$scope.$on("objectChanged", function (event, args) {
if (UtilsService.objectToDelete) {
$scope.ObjectToDelete = UtilsService.objectToDelete;
$scope.Message = UtilsService.deleteMessage;
$('#modalErrorDialog').modal('show');
}
});
$scope.DeleteObject = function () {
$scope.ObjectToDelete.Delete();
$scope.ObjectToDelete = null;
$('#modalErrorDialog').modal('hide');
}
$scope.Cancel = function () {
$scope.ObjectToDelete = null;
$('#modalErrorDialog').modal('hide');
}
},
templateUrl: '/shared/DeleteDialog'
};
}]);
I tried it with $watch before but that did not work for me.
The main thing i want to do is to place this directive on my Layout Page so i can call it from any controller with an object i want to delete.
I'm writing an AngularJS application and I'm searching for a way to unit test every single aspect.
In this particular case, I need to unit test a custom directive which I've written that represents a control.
The directive can be found here:
var officeButton = angular.module('OfficeButton', []);
officeButton.directive('officeButton', function() {
return {
restrict: 'E',
replace: false,
scope: {
isDefault: '#',
isDisabled: '#',
control: '=',
label: '#'
},
template: '<div class="button-wrapper" data-ng-click="onClick()">' +
'<a href="#" class="button normal-button">' +
'<span>{{label}}</span>' +
'</a>' +
'</div>',
controller: ['$scope', function($scope) {
var event = this;
var api = {
changeLabel: function(label) {
$scope.label = label;
},
enable: function() {
$scope.isDisabled = false;
},
disable: function() {
$scope.isDisabled = true;
},
setAsDefault: function() {
$scope.isDefault = true;
},
removeDefault: function() {
$scope.isDefault = false;
}
};
event.onClick = function() {
if (typeof $scope.control.onClick === 'function') { $scope.control.onClick(); }
};
$.extend($scope.control, api);
function Init() {
if ($scope.isDefault === 'true') { $scope.isDefault = true; }
else { $scope.isDefault = false; }
}
Init();
}],
link: function(scope, element, attributes, controller) {
scope.$watch('isDefault', function(value) {
if (value === 'true' || value) { $('a', element).addClass('button-default'); }
else { $('a', element).removeClass('button-default'); }
});
scope.onClick = function() { controller.onClick(); }
}
}
});
This directive can be called by using the following HTML snippet:
<office-button label="Office Web Controls" control="buttonController"></office-button>
Now, this directive exposes an API which functions such as changeLabel, enable, disable, ....
Now, those functions are not defined on the load of the application, meaning if at the bottom of my HTML I call the following code:
$scope.buttonController.changeLabel('Office Web Controls for Web Applications Directive Demo');
It will throw an error because the changeLabel() method is not defined.
In order to make it function, I need to wrap those calls in an angular.ready function, such as:
angular.element(document).ready(function () {
$scope.buttonController.changeLabel('Office Web Controls for Web Applications Directive Demo');
});
Here's a plunker for your information.
Now, I'm writing unit tests using Jasmine, and here's what I have for the moment:
describe('Office Web Controls for Web Applications - Button Testing.', function() {
// Provides all the required variables to perform Unit Testing against the 'button' directive.
var $scope, element;
var buttonController = {};
// Loads the directive 'OfficeButton' before every test is being executed.
beforeEach(module('OfficeButton'));
// Build the element so that it can be called.
beforeEach(inject(function($rootScope, $compile) {
// Sets the $scope variable so that it can be used in the future.
$scope = $rootScope;
$scope.control = buttonController;
element = angular.element('<office-button control="buttonController"></office-button>');
$compile(element)($scope);
$scope.$digest();
}));
it('Should expose an API with certain functions.', function() {
});
});
Now, in the it function, I would like to test if the $scope.control does expose the API as defined in the directive.
The problem is that the page needs to be ready before the API is available.
Any tought on how to change the code or how to unit test this correctly?
I've found the issue, it was just a wrong configuration on the unit test.
When using this code:
$scope.control = buttonController;
element = angular.element('<office-button control="buttonController"></office-button>');
I must change the element to:
$scope.control = buttonController;
element = angular.element('<office-button control="control"></office-button>');
I have following code in the "First" Ctrl of the my app, which is displaying timer for countdown.
Everything is working absolutely fine until i visited second page which is in the app.js defined by this way:
.state('app.home', {
url: '/home:playlistData',
views: {
'menuContent' :{
templateUrl: 'templates/home.html',
controller: 'HomeCtrl'
}
}
})
.state('app.saved', {
url: '/saved',
views: {
'menuContent' :{
templateUrl: 'templates/saved.html',
controller: 'SavedCtrl'
}
}
})
If i came back from second view to the first counter is still displayed but value in the
$scope.minutesLeft
Is not updated but
setInterval
function is still executing the code in the background and updated data values are still holded in the Dataholdingservice.
I tried scope apply and timeout functions, but without the luck.
Could somebody tell me how can i solve this issue?
Many thanks for any help.
Code of the HomeCtrl for countdown timer is following:
$scope.setTimer = function(timer) {
console.log(timer);
$scope.timer = timer;
};
$scope.saveTimer = function(timer) {
if($scope.selectedSounds.length == 0) {
$scope.showAlert("Add some sounds", "Cannot run timer for empty list");
} else {
$scope.clearCountDownAnimation();
var animationTimerId = setInterval(function () {
$("#minutesLeft").fadeTo(100, 0.1).fadeTo(200, 1.0);
}, 1000);
Dataholdingservice.setAnimationId(animationTimerId);
Dataholdingservice.setMinutesLeft(timer);
$scope.closePopover();
$scope.countDown();
}
};
$scope.clearCountDownAnimation = function() {
$("#minutesLeft").clearQueue().finish();
// Clear previously set animations
console.log(Dataholdingservice.getAnimationId());
if (Dataholdingservice.getAnimationId() != null) {
console.log("Interval cleared");
clearInterval(Dataholdingservice.getAnimationId());
}
};
$scope.countDown = function() {
var minutesLeft = Dataholdingservice.getMinutesLeft();
$scope.minutesLeft = minutesLeft;
$scope.isCounterDisplayed = Dataholdingservice.isCounterDisplayed();
var timerId = setInterval(function() {
console.log("Counting down");
minutesLeft -- ;
console.log("Decreasing minutes");
console.log(minutesLeft);
Dataholdingservice.setMinutesLeft(minutesLeft);
console.log("minutes left " + Dataholdingservice.getMinutesLeft());
$scope.$apply(function () {
$scope.minutesLeft = Dataholdingservice.getMinutesLeft();
});
if(minutesLeft <= 0) {
console.log("Time left");
clearInterval(Dataholdingservice.getTimerId());
clearInterval(Dataholdingservice.getAnimationId());
console.log(Dataholdingservice.isCounterDisplayed());
$scope.hideCounter();
$scope.stopAllSelectedSounds();
}
}, 1000 * 1);
Dataholdingservice.setTimerId(timerId);
};
$scope.hideCounter = function() {
console.log("Hidding the counter");
$scope.isCounterDisplayed = false;
$scope.$apply();
};
$scope.cancelTimer = function() {
clearInterval(Dataholdingservice.getTimerId());
clearInterval(Dataholdingservice.getAnimationId());
$("#minutesLeft").hide();
$ionicLoading.show({
duration: 500,
template: 'Timer canceled'
});
};
Since the $scope.minutesLeft is a primitive datatype, sometimes the changes happening in the controller will not get reflected in the view. You can create an object like $scope.viewModel = {} and then add the minutesLeft as a property to it like $scope.viewModel.minutesLeft = mintesLeft in your countdown function and bind viewModel.minutesLeft to the view. you should see the value getting updated properly.
I am not sure of your exact requirement, but I have put together the code for creating a simple timer that runs in the background in an angular service. The working code is available at http://codepen.io/svswaminathan/pen/MYXOPM
I use this controller in ionic app to check new message or activity and add a badge if is there news:
.controller('checkNew', function($scope, getNewMsg, getNewAct, $localstorage, $cordovaLocalNotification, $http, $state) {
getNewMsg.getNew(function(data) {if(data!=0){$scope.addActivity(data);$scope.counterAct = data}else{$scope.cancelAllNotification}});
getNewAct.getNew(function(data) {if(data!=0){$scope.addNotification(data);$scope.counter = data}else{$scope.cancelAllNotification}});
and this is for example my .factory getNewMsg:
.factory('getNewMsg', function($http, $localstorage, $timeout){
return {
getNew: function(callback){
var user_id = $localstorage.get('user_id');
var timer;
function myLoop(){
timer = $timeout(function(){console.log( "Timeout executed")},5000);
timer.then(
function(){$http.post('http://www.digitalxp.it/appwork/include/check_msg.asp?id='+user_id).success(function(data){callback(data)}).error(function(){alert("Errore di comunicazione!")});myLoop()},
function() {console.log("Timer rejected!")}
);
}
myLoop();
}}
})
my problem is that I've to call the controller each time to add the badge in the header, but I would check only one time and view the badge on all ion-view of the app! (also the side menu near message item!)
I think is it possible with directive, but I dont know how to start!
I've updated this to use broadcas as you've requested, now your controller will be registered to the event newMessageReceived and will update every 5 seconds. Also, this acts more as a service, so I've updated the module to be a service and not a factory.
Please note that I changed timer.then(setNew, handleError); to timer.then(setNew(), handleError);
.controller('checkNew', function($scope, messageService, getNewAct, $localstorage, $cordovaLocalNotification, $http, $state) {
//register to broadcast and update when the messages are updated
messageService.newMessageReceived($scope, updateMessage)
function updateMessage(message) {
if(message){
$scope.addActivity(message);
$scope.counterAct = message;
} else {
$scope.cancelAllNotification
}
}
});
.service('messageService', function($rootScope, $http, $localstorage, $timeout){
/* Initialization */
//setup timer to run every 5 seconds
var timer = $timeout(timeoutFunction, 5000);
//set the actions for resolve and reject
timer.then(setNew(), handleError);
function timeoutFunction() {
return;
}
function handleError() {
console.log('Timer rejected');
}
//every time the newMessageRecieved event is broadcasted, run the callback and register the scope
function newMessageReceived(scope, callback) {
callback(getNew());
scope.$on('newMessageReceived', function() {
callback(getNew());
});
}
function getNew() {
return $localstorage && $localstorage.message || undefined;
}
function setNew() {
var user_id = $localstorage.get('user_id');
return $http.post('http://www.digitalxp.it/appwork/include/check_msg.asp?id='+user_id)
.success(function(data){
$localStorage.message = data;
$rootScope.$broadcast('newMessageReceived');
})
.error(function(){alert("Errore di comunicazione!")})
}
return {
newMessageReceived: newMessageReceived
};
});