AngularJS HotTowel invoke spinner during ajax call - angularjs

I am trying to invoking the spinner during any xhr call within the application. Whereas the spinner appeared when I click at menu or route to different page.
Index page
<aside class="main-sidebar">
<!-- sidebar: style can be found in sidebar.less -->
<section class="sidebar">
<!-- Sidebar user panel (optional) -->
<div data-cc-sidebar data-ng-controller="sidebar as vm">
<ul class="sidebar-menu">
<li data-ng-repeat="r in vm.navRoutes">
</li>
</ul>
</div>
</section>
<!-- /.sidebar -->
</aside>
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper" data-ng-controller="shell as vm">
<!-- Content Header (Page header) -->
<section class="content-header"></section>
<!-- Main content -->
<section class="content">
<!-- Your Page Content Here -->
<div data-ng-show="vm.isBusy" class="page-splash dissolve-animation">
<div data-cc-spinner="vm.spinnerOptions"></div>
<div class="page-splash-message page-splash-message-subtle">{{vm.busyMessage}}</div>
</div>
<div data-ng-view class="shuffle-animation"></div>
</section><!-- /.content -->
</div><!-- /.content-wrapper -->
Shell.js
I have changed the shell.js file according to following instruction.
From
$rootScope.$on(events.spinnerToggle, function (data))
To
$rootScope.$on(events.spinnerToggle, function (event, data))
And found from following link comments
http://johnpapa.net/hot-towel-angular/
(function () {
'use strict';
var controllerId = 'shell';
angular.module('app').controller(controllerId,
['$rootScope', 'common', 'config', shell]);
function shell($rootScope, common, config) {
var vm = this;
var logSuccess = common.logger.getLogFn(controllerId, 'success');
var events = config.events;
vm.busyMessage = 'Please wait ...';
vm.isBusy = true;
vm.spinnerOptions = {
radius: 40,
lines: 7,
length: 0,
width: 30,
speed: 1.7,
corners: 1.0,
trail: 100,
color: '#F58A00'
};
activate();
function activate() {
logSuccess('SIPPRES loaded!', null, true);
common.activateController([], controllerId);
}
function toggleSpinner(on) { vm.isBusy = on; }
$rootScope.$on('$routeChangeStart',
function (event, next, current) { toggleSpinner(true); }
);
$rootScope.$on(events.controllerActivateSuccess,
function (data) { toggleSpinner(false); }
);
$rootScope.$on(events.spinnerToggle,
function (event,data) { toggleSpinner(data.show); }
);
};
})();
Directive looks like
app.directive('ccSpinner', ['$window', function ($window) {
// Description:
// Creates a new Spinner and sets its options
// Usage:
// <div data-cc-spinner="vm.spinnerOptions"></div>
var directive = {
link: link,
restrict: 'A'
};
return directive;
function link(scope, element, attrs) {
scope.spinner = null;
scope.$watch(attrs.ccSpinner, function (options) {
if (scope.spinner) {
scope.spinner.stop();
}
scope.spinner = new $window.Spinner(options);
scope.spinner.spin(element[0]);
}, true);
}
}]);
Thank you

I found my solution. Above code just working fine. Unfortunately, I placed my spinner.spinnerShow(); at wrong place in controller. Bellow controller function just working fine.
function updateColor(updatedColor) {
spinner.spinnerShow();
return datacontextSetting.updateColor(updatedColor).then(function (data) {
$scope.newColor = data;
$uibModalInstance.close($scope.newColor);
}, function (response) {
$scope.frmColor.$valid = false;
// Here is where we can catch the errors and start using the response.
if (!angular.isUndefined(response.statusCode)) {
$scope.errorMessage = response.statusCode + "\n";
}
if (response.modelState) {
for (var key in response.modelState) {
$scope.errorMessage += response.modelState[key] + "\n";
}
}
if (response.message) {
$scope.errorMessage += response.message;
}
}).finally(function () {
spinner.spinnerHide();
});
};
Thanks

Related

Ng-Click not working on mobile devices. How to change to ngTouch

I have a website which uses angular.js. The ng-click is working fine on laptop/desktop but not on mobile devices. From my research, I learned that i need to use ngTouch and I undertand that. My problem is that I am not a programmer and does not know how to do it.
I am hoping that there is someone who can help me or provide me with the right step or code. this is my code:
<div class="container" ng-controller="MessageBoardCtrl">
<div class="span6">
<div class="row-fluid item" ng-repeat="item in items" ui-animate>
<div class="span2"><img src="../images/post.png" width="48px" height="48px"/></div>
<div class=" well well-small">
<p>{{item.message}}</p>
</div>
</div>
</div>
<div class="span6">
<div class='well'>
<button class="btn btn-primary" ng-click="sendMessage()">Share</button>
Here is the javascript:
<script src="../templates/js/jquery.js"></script>
<script src="../templates/js/angular.js"></script>
<script src="../templates/js/angular-ui.js"></script>
<script src="../templates/js/angular-touch.js"></script>
<script src="../templates/js/angular-touch.min.js"></script>
<script>
function MessageBoardCtrl($scope, $http, $timeout) {
$scope.items = [];
$scope.message = '';
$scope.email = '';
$scope.lastTime = 0;
$scope.refreshMessages = function() {
$http.get('../templates/faucet.php/messages?time=' + $scope.lastTime).success(function(data) {
for(id in data) {
item = data[id];
$scope.items.unshift(item);
if($scope.lastTime<item.time)
$scope.lastTime = item.time;
}
});
}
$scope.sendMessage = function() {
if(!$scope.message)
return;
$http.post('../templates/faucet.php/add_message', {message: $scope.message, email: $scope.email}).success(function() {
$scope.message = '';
});
}
$scope.periodicRefresh = function() {
$scope.refreshMessages();
$timeout($scope.periodicRefresh, 5000, true);
}
$scope.refreshMessages();
}
</script>
Can someone give me a clean code based on the above that will work for ngtouch and instruction as well. Thanks in advance.
You can write your own directive for touch event. Below is an example directive to handle touch events. The directive below only fire the event in case of touch/long touch. scope.isMoved will prevent firing event when user tap on screen and move they finger around.
function directive($timeout) {
var dir = {
link: link,
restrict: 'A',
scope: {
onTouch: '&'
}
};
return dir;
function link(scope, element) {
scope.isMoved = false;
$timeout(function () {
// user start tap on screen
element.bind('touchstart', function () {
scope.isMoved = false;
});
element.bind('touchend click', function (evt) {
if (!scope.isMoved) {
scope.onTouch(evt);
}
});
//
element.bind('touchmove', function () {
scope.isMoved = true;
});
});
}
}
In HTML:
<a on-touch="someFunction()"> Touch</a>

Flickity carousel: Items pushed out of viewport with ng-repeat?

Im trying to use metafizzy's flickity framework to display content dynamically, using angulars ng-repeat.
But for some reason the items seem to get pushed out from the flickity-viewport when loaded onto the DOM. Anyone know why that happens and how to avoid it?
The gallery works fine When displaying static content inside it like this;
HTML : STATIC MARKUP EXAMPLE
<div ng-controller="FlickityCtrl">
<div id="main-content" class="gallery js-gallery">
<div class="gallery-cell"> Static Title </div>
<div class="gallery-cell"> Static Title </div>
<div class="gallery-cell"> Static Title </div>
</div>
..its When trying to populate the gallery with the help of angular's ng-repeat directive,that the gallery breaks.
HTML : MARKUP USING NG-REPEAT
<div ng-controller="FlickityCtrl" >
<div id="main-content" class="gallery js-gallery">
<div ng-repeat="chapter in chapters" ng-click="loadSubchapters(chapter.title)">
<h1 class="gallery-cell cell-card-bg">
{{ chapter.title | strip_namespace }}
</h1>
</div>
</div>
<hr>
<button ng-click="loadChapters()" >Load chapters</button>
<hr>
<ul>
<li ng-repeat="chapter in subchapters">
{{ chapter.title | strip_namespace }}
</li>
</ul><br />
<hr >
</div>
JAVASCRIPT
angular.module('FlickityApp', [])
.controller('flickityCtrl', ['$scope', '$timeout', function ($scope, $timeout) {
var updateUI = function(data) {
if (!data || !data.query) { return; }
$timeout(function() {
$scope.chapters = data.query.pages;
console.log(data);
});
};
$scope.loadChapters = function() {
mw.loader.using('mediawiki.api', function() {
(new mw.Api()).get({
action: 'query',
generator: 'categorymembers',
gcmtitle: 'Category:examplepage'
}).done(function(data) {
$timeout(function() {
$scope.chapters = data && data.query ? data.query.pages : {};
});
});
});
};
$scope.loadSubchapters = function(chapterTitle) {
mw.loader.using('mediawiki.api', function() {
(new mw.Api()).get({
action: 'query',
generator: 'categorymembers',
gcmtitle: chapterTitle
}).done(function(data) {
$timeout(function() {
$scope.subchapters = data && data.query ? data.query.pages : {};
});
});
});
};
}])
.filter('strip_namespace', ['$sce', function($sce){
return function(text) {
text = text.split(":");
return text.length > 1 ? text[1] : text[0];
};
}]);
.directive('flickity', [function() {
return {
restrict: 'E',
templateUrl: 'templates/view.html',
replace: true,
scope: { chapters: '=' },
link: function(scope, elem, attrs, ctrl) {
scope.$watch('chapters', function() {
elem.flickity({
// settings
});
});
}
};
}]);
angular.element(document).ready(function() {
angular.bootstrap(document, ['FlickityApp']);
var flkty = new Flickity('.gallery');
});
Link to flickity api : http://flickity.metafizzy.co/api.htm

AngularJS - ng-if runs digest loop

I am facing problem with infinite loop on loading the view. The data is loaded from an API call using ngResource in the controller. The view seems to be reloaded multiple times before rendering the view correctly. I use ng directives in the template calling scope methods and this seems to get into loop causing the view to be re-rendered.
Here is my Controller
.controller('IndexCtrl', ['$scope', '$stateParams', 'ProfileInfo',
function($scope, $stateParams, ProfileInfo) {
$scope.navTitle = 'Profile Information';
$scope.data = {};
ProfileInfo.query({
id: $stateParams.id
}).$promise.then(function(Profile) {
if (Profile.status == 200) {
$scope.data.Profile = Profile.data[0];
}else{
console.log(Profile.status);
}
}, function(error) {
console.log(error);
});
$scope.showImageBlock = function(object, image) {
if (object.hasOwnProperty('type') && object.type == 'image') {
imageReference = object.value;
var imageUrl;
angular.forEach(image, function(value, key) {
if (value.id == imageReference) {
$scope.data.imageUrl = value.graphic.url;
return;
}
});
}
return object.hasOwnProperty('type') && object.type == 'image';
};
$scope.showText = function(object) {
console.log('text');
return object.hasOwnProperty('type') && object.type == 'text';
};
}
])
And Here is my template
<ion-view cache-view="false">
<ion-nav-title>
{{navTitle}}
</ion-nav-title>
<div class="bar bar-subheader bar-light">
<h2 class="title">{{navSubTitle}}</h2>
</div>
<ion-content has-header="true" padding="true" has-tabs="true" class="has-subheader">
<div ng-repeat="profileInfo in data.Profile">
<div class="list">
<img ng-if="showImageBlock(profileInfo,data.Profile.images)" ng-src="{{ data.imageUrl }}" class="image-list-thumb" />
<div ng-if="showText(profileInfo)">
<a class="item">
{{profileInfo.name}}
<span ng-if="profileInfo.description.length != 0"class="item-note">
{{profileInfo.description}}
</span>
</a>
</div>
</div>
</div>
</ion-content>
Here is the output of console window when tried log the number of times showText function is called.
The actual result from ngResource call has only 9 items in array but it loops more than 9 times and also multiple loops. This happens for a while and stops. Could anyone please point me in the right direction in fixing it.
Thank you
Finally I ended up creating a custom directive which does the function of ng-if without the watchers which triggers the digest loop. It's not a pretty solution but it seems to do the job as expected. I copied the code of ng-if and removed the $scope watcher. Here is the custom directive.
angular.module('custom.directives', [])
.directive('customIf', ['$animate',function($animate) {
return {
multiElement: true,
transclude: 'element',
priority: 600,
terminal: true,
restrict: 'A',
$$tlb: true,
link: function($scope, $element, $attr, ctrl, $transclude) {
var block, childScope, previousElements;
value = $scope.$eval($attr.customIf);
if (value) {
if (!childScope) {
$transclude(function(clone, newScope) {
childScope = newScope;
clone[clone.length++] = document.createComment(' end customIf: ' + $attr.customIf + ' ');
block = {
clone: clone
};
$animate.enter(clone, $element.parent(), $element);
});
}
}
else {
if (previousElements) {
previousElements.remove();
previousElements = null;
}
if (childScope) {
childScope.$destroy();
childScope = null;
}
if (block) {
previousElements = getBlockNodes(block.clone);
$animate.leave(previousElements).then(function() {
previousElements = null;
});
block = null;
}
}
}
};
}]);
This allows us to use the customIf as follows
<div custom-if="showText(profileInfo)">

Trying to integrate Phonegap Connection API with AngularJS

I'm trying to use PhoneGap Connection API inside my Angular/Ionic project. What I want is to check the user's network and if it's NONE I want to show a specific div and if it's not NONE, show another div by using the ng-show directive.
Here is my code so far:
controller.js
.controller('LoginCtrl', function($scope, CheckConnection) {
$scope.net = CheckConnection.networkState();
})
.factory('cordovaReady', function() {
return function (fn) {
var queue = [];
var impl = function () {
queue.push(Array.prototype.slice.call(arguments));
};
document.addEventListener('deviceready', function () {
queue.forEach(function (args) {
fn.apply(this, args);
});
impl = fn;
}, false);
return function () {
return impl.apply(this, arguments);
};
};
})
.factory('CheckConnection', function(cordovaReady) {
return {
networkState: cordovaReady(function() {
var net = navigator.connection.type;
if(net == 'none') {
var internet = false;
} else {
var internet = true;
}
return internet;
})
};
})
view.html
<ion-nav-view name="login">
<div ng-hide="LoginCtrl.net">
NO CONNECTION {{net}}
</div>
<div ng-show="LoginCtrl.net">
CONNECTED {{net}}
</div>
</ion-nav-view>
I'm pretty new with Angular and PhoneGap so this issue may be very simple to solve but I just can't figure it out :( Any help will be much appreciated, thanks!
try this:
<ion-nav-view name="login" ng-controller="LoginCtrl">
<div ng-hide="net">
NO CONNECTION {{net}}
</div>
<div ng-show="net">
CONNECTED {{net}}
</div>
Ok, I found the solution! Angular has it's own way to check connection so I don't even need PhoneGap for this.
Here is my new controller.js:
.controller('LoginCtrl', function($window, $scope ) {
$scope.online = navigator.onLine;
$window.addEventListener("offline", function () {
$scope.$apply(function() {
$scope.online = false;
});
}, false);
$window.addEventListener("online", function () {
$scope.$apply(function() {
$scope.online = true;
});
}, false);
})
And view.html
<ion-nav-view name="login">
<div ng-hide="online">
NO CONNECTION
</div>
<div ng-show="online">
CONNECTED
</div>
</ion-nav-view>
Hope it helps someone else :D

why the ng-class not changed?

Examples of the problem:
http://jsfiddle.net/paloalto/DTXC2/
HTML:
<div ng-app="app">
<div id="wrapper" ng-controller="AppController" ng-class="showChatPanel">
<div id="tabBar" class="ui vertical icon menu inverted" ng-controller="TabBarController">
<a class="item switchChatBtn" data-tab="showChatWraper">Open Chat Panel</a>
</div>
<div id="chatWraper" class="ui segment">Chat Panel Opend!!</div>
</div>
</div>
Javascript:
angular.module('app', ['app.controllers']);
var controllers = angular.module('app.controllers', []);
controllers.controller('AppController', function AppController($scope, $log, $http) {
$scope.showChatPanel = '';
$scope.$on("switchChatPanel", function (event, msg) {
console.log(msg);
$scope.showChatPanel = msg;
console.log($scope.showChatPanel);
// $scope.$broadcast("switchChatPanel-done", msg);
});
$scope.$watch('showChatPanel', function(newVal, oldVal) {
if(newVal){
console.log('yeah! It is a newVal !!!');
} else {
console.log('still oldVal ');
}
});
});
controllers.controller('TabBarController', function TabBarController($scope, $log, $http) {
var tabBarItem =$('#tabBar > .item');
tabBarItem.click(function(){
var tabClass = $(this).data('tab');
console.log(tabClass);
$scope.$emit("switchChatPanel", tabClass);
});
});
CSS:
#chatWraper {
display:none;
}
.showChatWraper #chatWraper{
display:block;
}
=====
I finally solved it using jQuery, but I still wonder why angular not success.
controllers.controller('TabBarController',function TabBarController ($scope,$log,$http) {
var tabBarItem =$('#tabBar > .item');
var chatPanelOpen = false;
tabBarItem.click(function(){
var tabClass = $(this).data('tab');
if(!chatPanelOpen){
$('#wrapper').addClass(tabClass);
chatPanelOpen = true;
} else{
$('#wrapper').removeClass(tabClass);
chatPanelOpen = false;
}
})
})
https://gist.github.com/naoyeye/7695067
========
UPDATE
http://jsfiddle.net/paloalto/DTXC2/17/
You shouldn't be doing DOM manipulation like that in the controller. The correct way to do this is like this
<div ng-controller="TabBarController">
<div ng-click="toggleChatPanel()" ng-class="{tabClass: isChatPanelOpen}">
</div>
controllers.controller('TabBarController', function ($scope) {
$scope.isChatPanelOpen = false;
$scope.toggleChatPanel = function () {
$scope.isChatPanelOpen = !$scope.isChatPanelOpen;
};
});

Resources