Angular UI Grid callbacks not received - angularjs

I'm trying to get a callback from the Angular UI grid (after data is loaded/rendered) but it doesn't seem to work for me.
I'm following this which says I should listen the scope using $scope.$on() and I tried this which implies I can provide a dataUpdated() and init() delegate through the options.
In both cases I don't see anything in my console... The grid renders fine and I can also see a console entry stating: 'grid refresh'.
I'm loading the grid with the following options:
$scope.$on('ngGridEventRows', function (event) {
console.log('rendering');
});
$scope.$on('ngGridEventRows', function (event) {
console.log('rendering');
});
$scope.options = {
enableSorting: true,
//data: myData,
dataUpdated: function() {
console.log('dataUpdated');
},
init: function () {
console.log('init');
}
};
$scope.grids = [$scope.options];
The options are passed to the grid as follows:
<div class="container-fluid" ng-repeat="grid in grids">
<div ui-grid="grid" ui-grid-exporter></div>
</div>

Those events are related to ng-grid (probably version 2.x) while you are using ui-grid (which is a rename of ng-grid version 3.0).
I'd suggest you to give a look at the updated website ui-grid.info where you can find docs and tutorials about the current version.
Regarding your peculiar need sadly it seems there is no documented event you can use, but looking through the code I found the following:
$scope.grid.api.core.on.rowsRendered( $scope, function() );
You should put this in your gridOptions.onRegisterApi(...) and the function will be called upon rendering is complete (or so it seems looking at the source).
I'd suggest you to play a bit with it and see what it can do.

Related

Two way binding for angular strap

I'm fairly new to angularjs and am using angular strap to use some of their features like aside.
So basically I have a function like this:
$scope.myAside = function() {
$aside({
title: 'My Title',
show: true,
animation: 'am-slide-left',
placement:'left',
contentTemplate: 'views/aside.html'
});
};
and I wanted to know how to use the two binding to get 'show' to display on the page. I basically wanted something like this on the page:
The aside is open: {{myAside.show}}
I read through their setup but couldn't find anything about two binding and how to know if the aside modal is open or not.
Here is the plugin with the documentation about aside. http://mgcrea.github.io/angular-strap/#/asides
You need to use {{ myAside.$scope.$isShown }}
Here is a plnkr showing how this works.

Apply loading spinner during ui-router resolve

resolve property of $routeProvider allows to execute some jobs BEFORE corresponding view is rendered.
What if I want to display a spinner while those jobs are executed in order to increase user experience?
Indeed, otherwise the user would feel the application has been blocked since no view elements were displayed for some milliseconds > 600 for instance.
Of course, there was the way to define a global div element out of the current view to display in order to display the spinner thanks to the $scope.$rootChangeStart function.
But I don't want to hide the whole page with just a poor spinner in the middle.
I want some pages of my webapp differ regarding the way the loading is displayed.
I came across this interesting post containing the exact issue I described above:
That approach results in a horrible UI experience. The user clicks on
a button to refresh a list or something, and the entire screen gets
blanketed in a generic spinner because the library has no way of
showing a spinner just for the view(s) that are actually affected by
the state change. No thanks.
In any case, after I filed this issue, I realised that the "resolve"
feature is an anti-pattern. It waits for all the promises to resolve
then animates the state change. This is completely wrong - you want
your transition animations between states to run parallel to your data
loads, so that the latter can be covered up by the former.
For example, imagine your have a list of items, and clicking on one of
them hides the list and shows the item's details in a different view.
If we have an async load for the item details that takes, on average,
400ms, then we can cover up the load almost entirely in most cases by
having a 300ms "leave" animation on the list view, and a 300ms "enter"
animation on the item details view. That way we provide a slicker feel
to the UI and can avoid showing a spinner at all in most cases.
However, this requires that we initiate the async load and the state
change animation at the same moment. If we use "resolve", then the
entire async animation happens before the animation starts. The user
clicks, sees a spinner, then sees the transition animation. The whole
state change will take ~1000ms, which is too slow.
"Resolve" could be a useful way to cache dependencies between
different views if it had the option not to wait on promises, but the
current behaviour, of always resolving them before the state change
starts makes it almost useless, IMO. It should be avoided for any
dependencies that involve async loads.
Should I really stop using resolve to load some data and rather start loading them in the corresponding controller directly? So that I can update the corresponding view as long as the job is executed and in the place I want in the view, not globally.
You can use a directive that listens on $routeChangeStart and for example shows the element when it fires:
app.directive('showDuringResolve', function($rootScope) {
return {
link: function(scope, element) {
element.addClass('ng-hide');
var unregister = $rootScope.$on('$routeChangeStart', function() {
element.removeClass('ng-hide');
});
scope.$on('$destroy', unregister);
}
};
});
Then you place it on the specific view's loader, for example:
View 1:
<div show-during-resolve class="alert alert-info">
<strong>Loading.</strong>
Please hold.
</div>
View 2:
<span show-during-resolve class="glyphicon glyphicon-refresh"></span>
The problem with this solution (and many other solutions for that matter) is that if you browse to one of the routes from an external site there will be no previous ng-view template loaded, so your page might just be blank during resolve.
This can be solved by creating a directive that will act as a fallback-loader. It will listen for $routeChangeStart and show a loader only if there is no previous route.
A basic example:
app.directive('resolveLoader', function($rootScope, $timeout) {
return {
restrict: 'E',
replace: true,
template: '<div class="alert alert-success ng-hide"><strong>Welcome!</strong> Content is loading, please hold.</div>',
link: function(scope, element) {
$rootScope.$on('$routeChangeStart', function(event, currentRoute, previousRoute) {
if (previousRoute) return;
$timeout(function() {
element.removeClass('ng-hide');
});
});
$rootScope.$on('$routeChangeSuccess', function() {
element.addClass('ng-hide');
});
}
};
});
The fallback loader would be placed outside the element with ng-view:
<body>
<resolve-loader></resolve-loader>
<div ng-view class="fadein"></div>
</body>
Demo of it all: http://plnkr.co/edit/7clxvUtuDBKfNmUJdbL3?p=preview
i think this is pretty neat
app.run(['$rootScope', '$state',function($rootScope, $state){
$rootScope.$on('$stateChangeStart',function(){
$rootScope.stateIsLoading = true;
});
$rootScope.$on('$stateChangeSuccess',function(){
$rootScope.stateIsLoading = false;
});
}]);
and then on view
<div ng-show='stateIsLoading'>
<strong>Loading.</strong>
</div>
To further Pranay's answer this is how I did it.
JS:
app.run(['$rootScope',function($rootScope){
$rootScope.stateIsLoading = false;
$rootScope.$on('$routeChangeStart', function() {
$rootScope.stateIsLoading = true;
});
$rootScope.$on('$routeChangeSuccess', function() {
$rootScope.stateIsLoading = false;
});
$rootScope.$on('$routeChangeError', function() {
//catch error
});
}]);
HTML
<section ng-if="!stateIsLoading" ng-view></section>
<section ng-if="stateIsLoading">Loading...</section>
I'm two years late to this, and yes these other solutions work but I find it easier to just handle all this in a just a run block like so
.run(['$rootScope','$ionicLoading', function ($rootScope,$ionicLoading){
$rootScope.$on('loading:show', function () {
$ionicLoading.show({
template:'Please wait..'
})
});
$rootScope.$on('loading:hide', function () {
$ionicLoading.hide();
});
$rootScope.$on('$stateChangeStart', function () {
console.log('please wait...');
$rootScope.$broadcast('loading:show');
});
$rootScope.$on('$stateChangeSuccess', function () {
console.log('done');
$rootScope.$broadcast('loading:hide');
});
}])
You don't need anything else. Pretty easy huh. Here's an example of it in action.
In ui-router 1.0 $stateChange* events are deprecated. Use transition hook instead. See migration guide below for more details.
https://ui-router.github.io/guide/ng1/migrate-to-1_0#state-change-events
The problem with '$stateChangeStart' and '$stateChangeSuccess' is "$rootScope.stateIsLoading" doesn't get refreshed when you go back to last state.
Is there any solution on that?
I also used:
$rootScope.$on('$viewContentLoading',
function(event){....});
and
$rootScope.$on('$viewContentLoaded',
function(event){....});
but there is the same issue.

Replacing jQuery 'on' function in AngularJS

I have some legacy jQuery code that looks like the following:
$(document).on('replace', 'div', function (e, new_path, original_path) {
// do stuff here
});
I'm trying to figure out how to move this code into an AngularJS consistent approach. The code above is running when index.html gets loaded. I'm trying to move initialization code into a directive. Currently, I'm calling the directive as shown here:
<body initialize-page>
... content goes here
</body>
My directive looks like the following:
.directive('initializePage', function ($document) {
return {
restrict: 'A',
link: function (element) {
console.log('initialization code goes here.');
}
};
})
However, I don't know what the AngularJS equivalent of 'on' is. I'd like to get away from using jQuery if at all possible.
Thanks!
Angular includes a subset of jquery it calls jqLite. The jqlite version of .on has these constraints:
on() - Does not support namespaces, selectors or eventData
So, we can use the Angular on, but slightly differently than you did in jQuery (namely without the selector).
A directive's link function's second parameter is the element the directive is applied to. So, while we can't specify a selector in on, we can use find on the element parameter to get your div. Then we can chain the on to that result. This gives us the following link function:
link: function (scope,element,attrs) {
element.find('div').on('replace', function (event) {
console.log("got event: ",event);
});
};
Here's a demo fiddle in which I used click instead of replace just because it's easier to show.

How can I provide callbacks for an AngularStrap or Angular UI Bootstrap Popover, to execute on open and on close?

I am using the Angular UI Bootstrap popover ( http://angular-ui.github.io/bootstrap/#/popover ) and would like to specify a callback function to execute when it is opened and another for when it is closed. My use case is that I am using the popover to contain filters for a grid of data. I would like to load some remote filtering options on open, apply the selected filters only when the popover is closed.
The documentation only appears to support a few basic options but no indication of callback support. I'm not seeing anything in the source code either. Is my only option to set an interval function to check periodically if the popover is visible in the DOM?
I'm also considering Angular Strap's popover to achieve the same result, but can't seem to find the option to set callbacks there either.
Seems that Koni's and user2453461's answers no longer work as popover.js utilizes tooltip.js that does the emit event. You should be able to do the following:
$scope.$on('tooltip.show.before', function() {
console.log("show before");
});
$scope.$on('tooltip.show', function() {
console.log("show");
});
$scope.$on('tooltip.hide.before', function() {
console.log("hide before");
});
$scope.$on('tooltip.hide', function() {
console.log("hide");
});
Not sure how good an idea this is, though, as the syntax seems to change over versions.
I found a solution for AngularStrap. It emits popover-* events that I can hook into here:
https://github.com/mgcrea/angular-strap/blob/3c8bd1d37ab3b023bd79f9b0c1a6931b18e2ac84/src/directives/popover.js#L110
So using this in my popover controller I can hook into hidden/shown. This is for modals but works the same for the popover with the popover- prefix
https://github.com/mgcrea/angular-strap/issues/107#issuecomment-16701662
For me worked as user2453461 said:
$scope.$on('popover-shown', function() {
console.log("SHOWN");
});
$scope.$on('popover-hidden', function() {
console.log("HIDDEN");
});
Popover "inherits from" $tooltip, which $emits a tooltip.hide.before and then a tooltip.hide event. However, since it is $emiting and not $broadcasting, only the popover's scope (and its children, but it is unlikely to have any) has access to the event.
In my code (coffeescript), this looks like:
.directive 'ngPopoverParent', ($popover) ->
link: (scope, element, attrs) ->
popover = $popover element,
'title': attrs.title
'content': attrs.content
'animation': attrs.animation
'trigger': 'click'
popover.$promise.then ->
popover.$scope.$on 'tooltip.hide', ->
console.log('tooltip hidden!')
Relevant source:
https://github.com/mgcrea/angular-strap/blob/08167f7d5f52424b0f6fe40f3a053e134f550472/src/tooltip/tooltip.js

AngularJS directive timing issue

I'm trying to figure out the order of operations here, and I'm obviously failing.
I'm trying to implement a jquery image gallery plugin as a directive. The plugin takes a set of images, uses $.load to load them into the DOM, and then upon completion creates a nice gallery.
You can see my example here. If you click "Load Images", it will pull 20 images from Flickr. What I'd like to have happen is, on load, the $.gridnav function is called. I am demonstrating that functionality when you click "Display GridNav", but obviously don't want that second button click as a requirement.
Now, my directive sits outside of my ng-repeat which is bound to the property getting set on successful callback from flickr. So I understand why it's not getting called again, I'm just not sure what to do to make this function as I'd like.
I'm not entirely sure how gridnav is supposed to work, so I'm not sure if this works fully, but what about calling gridnav anytime the array changes? You can pass the array into the directive via the view and then $watch the vale on the scope inside the directive.
HTML:
<div class="well clearfix giftCardList tj_container" id="giftCardContainer"
gridnav="giftCards">
...
JS:
link: function ($scope, element, attrs) {
// watch for the value passed in to gridnav attribute to change
$scope.$watch(attrs.gridnav, function(value){
// don't do anything unless there's actually a value
if (!value) return;
// once it's changed, call gridnav on the next loop
$timeout(function() {
$(element).gridnav({
rows: 1,
navL: '#giftCard_prev',
navR: '#giftCard_next',
type: {
mode: 'disperse',
speed: 400,
easing: '',
factor: '',
reverse: ''
}
}, 0);
}, true);
});
}
Plunker: http://plnkr.co/edit/lMYvIJEkEPRAaNIAuNvF?p=preview

Resources