AngularJS application loader - angularjs

I am trying to create a loader, and after the partial has rendered, I want to fade away the loader and display my application.
I have created the loader like so:
app.directive(
"mAppLoading",
function ($animate) {
return ({
link: link,
restrict: "C"
});
function link(scope, element, attributes) {
// NOTE: Am using .eq(1) so that we don't animate the Style block.
$animate.enabled(true);
scope.$on('$viewContentLoading', function () {
if (!!element) {
$animate.leave(element.children().eq(1)).then(
// Remove the root directive element.
element.remove();
// Clear the closed-over variable references.
scope = element = attributes = null;
}
);
}
});
}
}
);
This code above will remove the loader and the page will show up, but at the wrong time. The page is still not loaded. Is there an event that I can use when the partial is done rendering inside <div ui-view></div>?

It is the $viewContentLoaded event which you should use to determine if the content is loaded successfully.
scope.$on('$viewContentLoaded', function () {
...
Here is a tutorial on this

Related

Scroll Bottom Angularjs Directive

I am working on chat app, and the problem seems to be that directive for scroll bottom, to the last item in ng-repeat is not working properly when there are images in messages.
So it just doesn't scroll all the way bottom.
I think the reason is that directive scrolls before the image is fully loaded.
// Scroll to bottom directive
.directive('schrollBottom', function ($timeout) {
return {
scope: {
schrollBottom: "="
},
link: function (scope, element) {
scope.$watchCollection('schrollBottom', function (newValue) {
if (newValue)
{
$timeout(function() {
$(element[0]).scrollTop($(element)[0].scrollHeight);
}, 0, false);
}
});
}
}
})
Check here to see how to wait for an image to load before doing something. You can add a class to all images and wait for all images with that class to load before scrolling.
I would keep what logic you have, and put it inside of an img.onload callback handler. Then any scrolling to the bottom will only occur once all images have loaded.

offset ui-router autoScroll

I am using ui-router's autoScroll to scroll down to a div (ui-view) when page loads/route change. It currently does so. Is there any way to offset autoScroll? I need to leave 100px above the element visible for menu and am confused on how to acchomplish this.
app.directive('scrollTop', function($uiViewScroll) {
var linker = function (scope, element, attr, $elm) {
$uiViewScroll(element);
};
return {
restrict: 'A',
link: linker
}
});
HTML:
<ui-view [autoscroll="true"]/>
autoscroll directive allows you to set the scroll behavior of the browser window when a view is populated.
By default, $anchorScroll is overridden by ui-router's custom scroll service, $uiViewScroll. This custom service let's you scroll ui-view elements into view when they are populated during a state activation.
Try to decorate the default $uiViewScroll service, overriding the default behaviour.
app.config(function ($provide) {
$provide.decorator('$uiViewScroll', function ($delegate) {
return function (uiViewElement) {
// var top = uiViewElement.getBoundingClientRect().top;
// window.scrollTo(0, (top - 30));
// Or some other custom behaviour...
};
});
});
See http://corpus.hubwiz.com/2/angularjs/22290570.html

AngularJS watcher not binding/ watching

I'm trying to implement the "infinite-scroll" feature in a list using a directive, which should load progressively a new set of "orders" when the scroll of the html element reaches or exceeds 75% of the scrollable height and append it to the existing list.
Unfortunately, the watcher doesn't trigger when i scroll the list.
The directive is located in the right tag and the watcher triggers the listener function only the first time, when the element is rendered by the browser.
The strange thing is that if i change path and then i return to the path where the list is, the watcher start behaving correctly and trigger the listener function everytime i perform a scroll.
<ol orders-loader class="orders-list">...</ol>
angular:
(function () {
angular.
module('myApp')
.directive('ordersLoader', ['$window', '$timeout', 'ordersResource', ordersloaderDirective])
function ordersloaderDirective($window, $timeout, loading, ordersResource) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
scope.orders = ordersResource; /*ordersResource use $resource to api calls
and then stocks the data in a array exposed in the scope*/
$timeout(function () {
scope.$watch(function () { return element[0].scrollTop }, function () {
if (*the scroll exceedes more or less 75% of the total scrollHeight*/) {
/*asking for more orders*/
}
});
}, 0);
}
}
}
I can't figure out where is the problem.
Solved
As yeouuu suggested, there was no digest cycle after the list scroll event, so i added:
element.bind('scroll', function () {
scope.$apply();
});
just before the $timeout function.
Whenever using plugins outside of angularJs that should trigger watcher you need to explicitly apply them. Otherwise Angular won't be aware of these changes/events.
In your case that means adding scope.$apply(); after the event.
Your edited solution:
element.bind('scroll', function () {
scope.$apply();
});
More information can be found here about the scope life cycle: https://docs.angularjs.org/guide/scope#scope-life-cycle

Create a nested fallback src for an Image using angularjs directive

In my tag if the src returns a 404 then I can display a fallback image using directives, but if this fallback image is also returns 404 the how can I show another image using directive
Create a directive to go through a series of error images and provide them one after the other.
You can provide alternative image urls in the tag itself.
<img fallbacksrc="http://url1/error.jpg,http://url2/error.jpg" src="http://url0.image.jpg">
Then write a directive for fallbacksrc and bind the tag for error event. Use split function to alternative images in to an array. You can then choose from this array inside the link function.
The information you are looking for is that the error event will occur any number of times as long as the src fails. So there is no limit for this to occur if all the images you are setting inside the directive fails continuously.
Here is a sample code. I'm using an array of error images in the scope itself in this example without providing them inside the tag.
function MyCtrl($scope) {
$scope.image = "http://greentreesarborcareinc.com/wp-content/uploads/2014/01/image-placeholder.jpg1"
$scope.errorImageIdx = 0;
$scope.errorImages = ["http://spanning.com/assets/uploads/images/11954453151817762013molumen_red_square_error_warning_icon.svg_.med_.png", "http://fivera.net/wp-content/uploads/2014/03/error_z0my4n.png"]
}
myApp.directive('fallbacksrc', function() {
return {
link: function(scope, ele) {
ele.bind('error', function() {
if (scope.errorImageIdx <= scope.errorImages.length - 1) {
angular.element(this).attr("src", scope.errorImages[scope.errorImageIdx]);
scope.errorImageIdx++;
}
});
}
}
});
Here the tag will try to display the image referenced in $scope.image. But that is invalid. So, it tries to load the images from the array.
Try setting the first element of the array to something invalid. It will automatically select the second image in this case.
You can create angular directive like this -
app.directive('onError', function() {
return {
restrict:'A',
link: function(scope, element, attr) {
element.on('error', function() {
element.attr('src', attr.onError);
})
}
}
});
And use like -
<img class="pic" on-error="default-image.jpg" ng-src="{{author.profileImageUrl}}">

AngularJS directives and require

I have this directive that is getting more and more complicated. So I decided to split it up into parts.
The directive itself loaded a garment SVG graphic, when the SVG loaded it then ran a configure method which would apply a design, applied picked colours (or database colours if editing) and other bits and pieces.
As I said, it was all in one directive, but I have now decided to separate the logic out.
So I created my first directive:
.directive('configurator', function () {
// Swap around the front or back of the garment
var changeView = function (element, orientation) {
// If we are viewing the front
if (orientation) {
// We are viewing the front
element.addClass('front').removeClass('back');
} else {
// Otherwise, we are viewing the back
element.addClass('back').removeClass('front');
}
};
return {
restrict: 'A',
scope: {
garment: '=',
onComplete: '&'
},
require: ['configuratorDesigns'],
transclude: true,
templateUrl: '/assets/tpl/directives/kit.html',
link: function (scope, element, attrs, controllers) {
// Configure our private properties
var readonly = attrs.hasOwnProperty('readonly') || false;
// Configure our scope properties
scope.viewFront = true;
scope.controls = attrs.hasOwnProperty('controls') || false;
scope.svgPath = 'assets/garments/' + scope.garment.slug + '.svg';
// Apply the front class to our element
element.addClass('front').removeClass('back');
// Swaps the design from front to back and visa versa
scope.rotate = function () {
// Change the orientation
scope.viewFront = !scope.viewFront;
// Change our view
changeView(element, scope.viewFront);
};
// Executes after the svg has loaded
scope.loaded = function () {
// Call the callback function
scope.onComplete();
};
}
};
})
This is pretty simple in design, it gets the garment and finds the right SVG file and loads it in using ng-transclude.
Once the file has loaded a callback function is invoked, this just tells the view that it is on that it has finished loading.
There are a few other bits and pieces that you should be able to work out (changing views, etc).
In this example I am only requiring one other directive, but in the project there are 3 required directives, but to avoid complications, one will suffice to demonstrate my problem.
My second directive is what is needed to apply the design. It looks like this:
.directive('configuratorDesigns', function () {
return {
restrict: 'A',
controller: 'ConfiguratorDesignsDirectiveController',
link: function (scope, element, attrs, controller) {
// Get our private properties
var garment = scope.$eval(attrs.garment),
designs = scope.$eval(attrs.configuratorDesigns);
// Set our controller designs array
controller.designs = designs;
// If our design has been set, watch it for changes
scope.$watch(function () {
// Return our design
return garment.design;
}, function (design) {
// If we have a design
if (design) {
// Change our design
controller.showDesign(element, garment);
}
});
}
}
})
The controller for this directive just loops through the SVG and finds the design that matches the garment design object. If it finds it, it just hides the others and shows that one.
The problem I have is that this directive is unaware of the SVG loading or not. In the "parent" directive I have the scope.loaded function which is executed when the SVG has finished loading.
The "parent" directive's template looks like this:
<div ng-transclude></div>
<div ng-include="svgPath" onload="loaded()"></div>
<span class="glyphicon glyphicon-refresh"></span>
So my question is this:
How can I get the required directives to be aware of the SVG loaded state?
If I understand your question correctly, $rootScope.broadcast should help you out. Just broadcast when the loading is complete. Publish a message from the directive you are loading the image. On the directive which needs to know when the loading is complete, listen for the message.

Resources