AngularJS - how do you know when angular has finished processing a page? - angularjs

How to know when all interpolation and processing on a given page has been completed?
compile: function (tElement, tAttrs) {
return function (scope, element, attrs) {
element.html(tpl);
$compile(element.contents())(scope);
};
},
This is not synchronous. If there are {{stuff}} and ng-repeat="..." etc... they will not all be guaranteed to be completed when the link function returns.
I need a way to know when the page has rendered and there's no more directives to process so that I can then use #hashes to navigate to elements created on the page by angular. (using $anchorScroll)

Maybe try this:
$scope.$on('$viewContentLoaded', function() {
// ....
});

There is a directive in angular for this very specific reason ; ngCloak.
The ngCloak directive is used to prevent the Angular html template
from being briefly displayed by the browser in its raw (uncompiled)
form while your application is loading. Use this directive to avoid
the undesirable flicker effect caused by the html template display.
Doc # https://docs.angularjs.org/api/ng/directive/ngCloak
Similar Question on Quora; How do I hide a div with AngularJS until ng-repeat has finished processing? # https://www.quora.com/How-do-I-hide-a-div-with-AngularJS-until-ng-repeat-has-finished-processing
This is another way to do it, but I prefer to use angular directive.
<li ng-repeat="opt in menuItems" my-custom-repeat-listener> </li>
and then on the directive something sort of like
if(scope.$last) { /*fire event or etc to show the list items*/ }

Related

Run 'ng-click' inside a directive's isolated scope

Thanks in advance for taking the time to look into this question
I have serverside generated code that renders a directive wrapped around pre-rendered content.
<serverside-demo color="blue">
<p><strong>Content from Server, wrapped in a directive.</strong></p>
<p>I want this color to show: <span ng-style="{color: color}">{{color}}</span></p>
<button ng-click="onClickButtonInDirective()">Click Me</button>
</serverside-demo>
This means that 1.) the directive tag, 2.) the content inside the directive tag, 3.)the ng-click and 4.) The curly braces syntax are all generated by the server.
I want AngularJs to pick up the generated code, recompile the template and deal with the scope.
The problem is that I am having trouble getting it working. I understand that because the ng-click is inside the controller block, it is picked up not by the directive isolated scope, but the parent controllers. Instead I want the opposite... to pick up the onClickButtonInDirective scope function inside the serversideDemo link
I have created a jsfiddle best explaining my problem, which aims to clearly demonstrate the working "traditional" way of loading the template separately (which works) comparing it to the server-side way.
https://jsfiddle.net/stevewbrown/beLccjd2/3/
What is the best way to do this?
Thank you!
There are two major problem in your code
1- directive name and dom element not matched, - missing in dom element
app.directive('serverSideDemo', function() {
use <server-side-demo color="blue"> instead of <serverside-demo color="blue">
2- you need to compile the html code of server-side-demo dom with directive scope in link function
$compile(element.contents())(scope);
Working jsfiddle
Use templateUrl instead of template to fetch the content of directive from server:
app.directive('serverSideDemo', function() {
return {
restrict: 'AE',
scope: {
color: '='
},
templateUrl: 'link/that/returns/html/content',
link: function(scope, element, attrs) {
scope.onClickButtonInDirective = function() {
console.log('You clicked the button in the serverside demo')
scope.color = scope.color === 'blue' ? 'red' : 'blue';
}
}
};
});
Have a look at angular docs for more details

Add event handler to DOM element that is created after a $resource get (AngularJS)

I'm having a hard time enabling a Bootstrap's popover component to my dom elements.
I'm working with AngularJS and on my template, I am using the ng-repeat directive to create a gallery.
<div ng-repeat="phone in phones" >
<a class="thumb" href="#/phones/{{phone.id}}">
<img class="img-responsive phone_image" ng-src="{{phone.image_path}}" data-content="{{phone.text}}" rel="popover" data-placement="right" data-trigger="hover">
</a>
</div>
On my template controller, I'm fetching the phones data from a third party API and than injecting it to the scopes variable "phones", like so:
phoneControllers.controller('PhoneListCtrl', ['$scope', 'Phones',
function ($scope, Cards) {
// Phones is the service that queries the phone data to the API
Phones.query(function(data){
// Got a response, add received to the phones variable
$scope.phones = data;
// for each .card_image element,give it the popover property
$('.phone_image').popover({
'trigger': 'hover'
});
});
}]
);
My problem lies with the $('.phone_image').popover segment. My thought was that by doing it inside the query's callback function it would work, since that's when the ".phone_image" elements are created. However it doesn't.
I seem to be failing to understand exactly in what scope should I assign the .popover property. I know it works because if I do it on the developer tools console, after all page content has been loaded, it works properly. I just don't know where to call it in my code to begin with.
Thanks in advance
It's happening because you are manipulating the DOM inside a controller. You should not do this, as the documentation says:
Do not use controllers to:
Manipulate DOM — Controllers should contain only business logic. Putting any presentation logic into Controllers significantly affects its testability. Angular has databinding for most cases and directives to encapsulate manual DOM manipulation.
In other words, when you use an Angular controller, you're just delegating the DOM manipulation to Angular through $scope databinding.
If you would like to manipulate the DOM, you should rely on directives. In your case, you can create a phonePopover directive like this:
angular
.module('phone', [])
.directive('phonePopover', function() {
return {
restrict: 'A',
replace: false,
scope: {
phoneText: '=phonePopover'
},
link: function(scope, element, attr) {
element.popover({
'trigger': 'hover',
'placement': 'right',
'content': scope.phoneText
});
}
});
And apply it to your element as following:
<img data-phone-popover="{{phone.text}}" class="img-responsive phone_image" ng-src="{{phone.image_path}}">

AngularJS loading message/please wait

How can I achieve the following in AngularJS:
If I have my page that will show some widgets and each widget has a button called refresh. When the button is clicked then the content of that widget is reloaded from server. While the content is reloaded I want to show the user a message within that widget, please wait ... with a possible fading effect.
How can I achieve that in AngularJS?
I kind of taught about having a common service for that purpose and then somehow each widget controller will use that service or something like that, maybe also a directive that will show the actual loading/please wait message?
What is your advise?
P.S. There should be also a loading/please wait message with fading for the whole page, when the route is changing ... like switching between pages.
In my recent project I'm using https://github.com/cgross/angular-busy
Very nice thing, all you have to do is put your promise into $scope, and then add cg-busy attr to your element which should have spinner (beside registering module obviously):
Controller:
$scope.myPromise = restangular.get('something',12).then(function(response) { ... })
Html:
<div cg-busy="myPromise"></div>
You can also customize template that's gonna be displayed (which includes spinner and text message).
there are implementations of this available on github : angular-spinner or angular-sham-spinner. Read this BLOG which details how the spinner works with angularjs
if you want to implement it yourself to be reusable...
app.directive("spinner", function(){
return: {
restrict: 'E',
scope: { enable: "=" },
template: '<div class="spinner" ng-show="enable"><img src="content/spinner.gif"></div>'
}
});
i havent tested the code but directive wont be more complex than above...
As harish point out the right way would be a directive, but theres no need if you want of include another dependency, you could do something like this
You can create a nice CSS3 only loading (so not images required) animation with the help of CssLoad
Create a directive with a linking function so you can call and stop the animations within your controller the angular way:
.directive('appLoading', function(){
return {
restrict: 'E',
templateUrl: 'template-file.html', // or template: 'template html code inline' Display none to the code is important so is not visible if youre not caling the methods
replace: true,
link: function(scope, elem) {
scope.$on('app-start-loading', function(){
elem.fadeIn(); //asumming you have jquery otherwise play with toggleClass and visible and invisible classes
});
scope.$on('app-finish-loading', function(){
elem.fadeOut();
});
}
}
})
Include in your html code the directive: <app-loading></app-loading>
Now all you need to do is call the scope methods in your controller like this:
$scope.$broadcast('app-start-loading'); // to start the loading animation
$scope.$broadcast('app-finish-loading'); // to stop the animation
NOTE: if all your widgets share a scope, the loading may be triggered in all of them

Should I make functions using $scope or directive?

I made some tests in angular to display an alert and both worked just fine.
This example:
<a href="" test>Foo</a>
js (outside controller):
myApp.directive('test', function($compile){
return function(scope, element, attrs){
element.bind('click', function(){
alert('Foo');
});
}
});
and this example:
Foo
js (inside controller):
$scope.test = function(){
alert('Foo');
}
Like I said, both worked. But, which one is correct for manipulating DOM and event handlers?
I'd say that you should use directives when you want to make it a component for reuse for example.
That's okay to use ng-click for a simple alert, but imagine when more DOM manipulation is needed. In this case a directive is a better solution.
This is a good read about AngularJS directives: The Hitchhiker’s Guide to the Directive.

Fire an event immediately after $scope.$digest

In my AngularJS app, there's several points at which I want to wait for a $scope to be processed into the DOM, and then run some code on it, like a jQuery fadeIn, for example.
Is there a way to listen for a "digestComplete" message of some sort?
My current method is: immediately after setting whatever $scope variables I want rendered, use setTimeout with a delay of 0 ms, so that it will let the scope finish digesting, and then run the code, which works perfectly. Only problem is, I very occasionally see the DOM render before that setTimeout returns. I'd like a method that is guaranteed to fire after digest, and before render.
In this jQuery fade-in-and-out fiddle (which I found it on the JSFiddles Examples wiki page), the author defines a "fadey" directive and performs the jQuery fadeIn (or fadeOut) in the directive's link function"
<li ng-repeat="item in items" fadey="500">
...
myApp.directive('fadey', function() {
return {
restrict: 'A',
link: function(scope, elm, attrs) {
var duration = parseInt(attrs.fadey);
if (isNaN(duration)) {
duration = 500;
}
elm = jQuery(elm); // this line is not needed if jQuery is loaded before Angular
elm.hide();
elm.fadeIn(duration)
Another possible solution is to use $evalAsync: see this comment by Miško, in which he states:
The asyncEval is after the DOM construction but before the browser renders.
I believe that is the time you want to attach the jquery plugins. otherwise
you will have flicker. if you really want to do after the browser render
you can do $defer(fn, 0);
($defer was renamed $timeout).
However, I think using a directive (since you are manipulating the DOM) is the better approach.
Here's a SO post where the OP tried listening for $viewContentLoaded events on the scope (which is yet another alternative), in order to apply some jQuery functions. The suggestion/answer was again to use a directive.
Alternatively, this example will work the same way as an AngularJS built-in ng-show directive, except it will fade-in or fade-out based on AngularJS condition:
<li ng-repeat="item in items" ng-ds-fade="{condition}">
<!-- E.g.: ng-ds-fade="items.length > 0" -->
...
myApp.directive('ngDsFade', function () {
return function(scope, element, attrs) {
element.css('display', 'none');
scope.$watch(attrs.ngDsFade, function(value) {
if (value) {
element.fadeIn(200);
} else {
element.fadeOut(100);
}
});
};
});
Source:
http://www.codeproject.com/Articles/464939/Angular-JS-Using-Directives-to-Create-Custom-Attri
If all you want is to run some jQuery stuff why not try the Angular-UI jQuery Passthrough?

Resources