AngularJS event which is triggered after all scopes are applied - javascript

I have a AngularJS application where I am loading data from a REST service.
Now what sometimes happens is that the brackets {{}} used to access values from scope are rendered and after that replaced by the real values. Now what I d like to do is add a ng-switch to the top DIV of the application and check whether a global var (e.g. pageLoaded (true|false)) is true or false. If its true, I d like to load the normal page, if its false, I d like to print something like "loading...". So my problem is how can I get notified (e.g. through a Angular Event) if all the data is ready, and is added to scope? Because after that I dlike to set pageLoaded to true.
Is there a way to do this in a generic way? I don't like to implement this per page.
Thanks a lot in advance.
Greets
Marc

You should use ng-cloak for that - http://docs.angularjs.org/api/ng.directive:ngCloak
For showing a loading panel, you can do something like:
<div ng-hide="true">Loading...</div>
So when angular finishes loading, ng-hide will occur and hide the loading panel.

Use ng-cloak to get rid of this sort of problems. And make sure you apply the ng-cloak directive to the body of the page so that this doesn't show up till the data is loaded properly. And use the following styling in your CSS file.
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
display: none;
}
Note: you can even create some other element or div, thats something like a popup or notification bar, which shows "please wait till the data is comnpletely loaded". Set this div to display:none initially and in the Ajax call, change the display property to block/inline as needed and finally make it dispay:none after the success call back.. :)

One of the solutions is you can use ng-bind instead of using {{}} which will show ugly {{}} when the value is not rendered.
<div ng-bind="value">Loading ...</div>

For anyone who is having a problem more to do with the actual question than OP's specific scenario:
I had a fragment that was getting loaded-in after/by the main partial that came in via routing.
I needed to run a function after that subpartial loaded and I didn't want to write a new directive and figured out you could use a cheeky ngIf
Controller of parent partial:
$scope.subIsLoaded = function() { /*do stuff*/; return true; };
HTML of subpartial
<element ng-if="subIsLoaded()"><!-- more html --></element>

Related

change an angularjs nested template at run time

I have a template which is nested inside another template which I want to load when i click on a button.
So the nested template is loaded dynamically. This is what I have done so far.
This is the main body.html (this loads when a url is provided in the browser e.g. http://url#/newtemplate)
<div ui-view> </div>
Other section of the code has been removed for brevity
This is the new_template.html which I expects it to show when I click a button.
When I put a template name directly like below i.e. when I hard code it
<div ui-view="number1"></div>
It loads the template fully.
This is the dynamic model
<button ng-model="template_name" ng-value="number1">Button1</button>
<div ui-view="{{template_name}}"></div>
{{template_name}}
The above does not load the template as I expected. but it shows the string number1 when
the button is clicked
What can I do for it to load the template....
This is my controller
.state('parent',{
url: '/newtemplate',
views:{
'':{
templateUrl: "parent.tpl",
contoller:"controller",
},
'number1#parent':{
templateUrl:"number1.tpl",
contoller:"formcontroller"
},
'number2#parent':{
templateUrl:"number2.tpl",
contoller:"formcontroller"
},
'number3#parent':{
templateUrl:"number3.tpl",
contoller:"formcontroller"
}
}
})
Strange enough when I used the dot notation it did not work so I have to use the absolute naming method.
I also noticed that when I added the nested views as shown above the time it takes before the template gets loaded take a very long time.
Please I would appreciate any help which can allow me to load a nested view at runtime (possibly very fast)
Expecting more answer
I still hope that the I can make use of ui-view/ui-router because of the ability to make use of controller.
I'm not sure you can use uiView to load html dynamically.
I would try another possible solutions:
Use directives
Using ngInclude
I'll leave you an example with ngInclude: https://next.plnkr.co/edit/M5hl71mXdAGth2TE?open=lib%2Fscript.js&deferRun=1&preview

Appending angular directives to the DOM after document is already loaded doesn't work with ngAnimate

I am working on creating an Angular service that will append a simple notification box to the DOM and display it, without having to add HTML code and write the logic to hide and show it when necessary.
The service is called $notify and is used as below:
$notify.error( "this is an error", {position: "bottom-left"} );
The service will use angular.element to build the notification box and add it to the DOM. All of this works great. However, I am also using ngAnimate and animate.css to have the notification smoothly slide in on show and slide out upon closing. I've verified that the animations work if I simply paste the notification HTML code into my page but will not work when the code is added dynamically via the service. Do items have to be in the DOM at document load for ngAnimate to work? I've verified that the Angular service is loaded and properly inserting the HTML code but no animations are being applied. Here's what the HTML and CSS look like.
HTML:
<div class="simple-notify simple-notify-top-left simple-notify-info" ng-if="toggle">
<simple-notify-header>Hello!<span class='simple-notify-dismiss pull-right' ng-click='doSomething()'>×</span></simple-notify-header>
<simple-notify-body>Some bogus text here!</simple-notify-body>
</div>
CSS/LESS:
.simple-notify {
&.ng-enter {
display:none;
animation: #animate-enter #animation-length;
-webkit-animation: #animate-enter #animation-length;
}
&.ng-enter-active {
display:block;
}
&.ng-leave {
animation: #animate-leave #animation-length;
-webkit-animation: #animate-leave #animation-length;
}
}
Thanks!!!
You should never modify elements on the page from a service, this is what a directive is for. You should create an attribute directive on your body HTML element and in that place your logic. You can use $rootScope.$broadcast from your $notify service and $scope.$on in your directive. Alternatively, you can scrap the $notify service altogether and just use $rootScope.$broadcast as it can accept as many arguments as you want.
Lastly, you'll need to use the $compile service to make your HTML run properly after you've added it to the body. The $compile is what turns raw DOM into code Angular will process.
From inside your directive's scope.$on handler in the link function, you'd have something like.
$compile('<div>template code here</div>')(scope, function(cloned, scope){
element.append(cloned);
});
But you'd also need a cleanup as well. You might just add the code once as the directive's template and show/hide it with different content instead of adding/removing the DOM.

DOJO Custom Dialog Box - does not parse template file

I am new to DOJO. I have a custom widget , which uses a template file for the dialog box contents.
I am extending dijit.Dialog in the script file.
dojo.declare(
"custom.credentials",
[dijit._WidgetBase, dijit._Templated,dijit._WidgetsInTemplateMixin,**dijit.Dialog**],
{
templatePath: dojo.moduleUrl("custom", "templates/credentials.html"),
....
....
postCreate: function() {
this.inherited(arguments);
alert(this.containerNode);
alert(this.mainDIV);
},
});
My Template test file looks like this
<div data-dojo-attach-point="mainDIV">
Login Dialog Box template here
</div>
For some reason, when I alert on this.mainDIV, I get 'undefined'. It does not read the template file. Also, this.containerNode gives me 'HTMLDIVElement', (parent dijit dialog DIV).
I am not able to figure out after a lot of trial error where exactly the issue is. Any help would be greatly appreciated.
Calling code
function opnPop(){
var pop= dijit.byId("customPopup");
pop.show();
}
<div dojoType="custom.credentials" id="customPopup"/>
Note : *When dijit.Dialog is not extended* it reads the template file without any problem, I.e, I am able to access this.mainDIV.innerHTML , that contains my own inner html contents.
Thank you.
If Dialog has to be sub-classed, then it must be the base class. Here, it seems that it is used as a mixin. Anyways, the problem is with the template that is used.
The template will be parsed and used by the code in Dialog. So, the template mentioned here has nothing but a div element with an attach point. There is no "containerNode" element (ie. attach point) and you are trying to access it in your js code, which will give error.
More important, the "titleBar" & "titleNode" elements are also missing form template, which will give errors while parsing the template. In order to avoid that, the code part that uses these elements need to be removed from js, to avoid error. So the widget creation will be successful. Try with the standard dijit.Dialog's template.
Add the data-dojo-attach-point="mainDIV" to the top level Dialog's div in the template.
In template, more things can be added, which won't cause any issues. But, if removed anything, will cause problem. If we are sub-classing a class/widget, we need to comply to the existing code.

jQuery Mobile "enhance" dynamically re-generated html

jQuery Mobile 1.2.0
I generate the HTML using JavaScript ($(selector).html(content)), add it to the DOM and then display it ($.mobile.changePage()).
Then I invoke an AJAX call, get some data, and re-generate the html (but the parent element, the same $(selector), stays the same, I just change its html(...)).
At this poing the HTML is not "enhanced" by jQM, no styling applied on it.
Now according to the docs I should simply call the page() function on the parent element, i.e $(selector).page().
Other places in the docs suggest triggering the create event, i.e $(selector).trigger("create").
The problem is that non of the above two methods works - the styling of jQM is not applied.
Looking at the code of jQM, I've tried triggering the pagecreate event on that element and it does work, but, this is not documented anywhere, so I'm uncertain of it, especially concerning future releases of jQM.
At some poing in the docs I've read that I can call page() on a page only once..
Anyway, is there any concise/standard way to tell jQM to "enhance" the whole element and its child-elements? Or should I simply stay with triggering the pagecreate event?
Thank you!
To recreate a whole page use this:
$(selector).trigger("pagecreate");
This was my answer to a simmilar question: https://stackoverflow.com/a/14011070/1848600. There's an example of page recreation. Take a look, this should probably solve your problem.
What is the scope of
$(selector).trigger("create");
You should be able to add any elements on the 'pagecreate' event which comes right before 'pageshow' jqm styling is applied to elements. For example I dynamically add a header/footer like this
$(document).on('pagecreate', "[data-role=page]", function() {
var header = "<div data-role='header'>some header stuff</div>";
var footer= "<div data-role='footer'>some footer stuff</div>";
$(this).prepend(header);
$(this).append(footer);
$("[data-role=header]").fixedtoolbar({tapToggle: false});
$("[data-role=footer]").fixedtoolbar({tapToggle: false});
});
Make sure you're using jquery 1.7 or above I think that's when the on method was introduced;
It sounds like you may be generating the DOM and then changing the page, try it the other way around go to the page first then dynamically edit the dom.
EDIT
set the reload page option to true
$.mobile.changePage($(page), {reloadPage: true});
Edit 2
$(selector).children().each(function(){
$(this).trigger('create');
})

Angularjs - ng-cloak/ng-show elements blink

I have an issue in angular.js with directive/class ng-cloak or ng-show.
Chrome works fine, but Firefox is causing blink of elements with ng-cloak or ng-show.
IMHO it's caused by the converting ng-cloak/ng-show to style="display: none;", probably the Firefox javascript compiler is little bit slower, so the elements appears for a while and then hide?
Example:
<ul ng-show="foo != null" ng-cloak>..</ul>
Though the documentation doesn't mention it, it might not be enough to add the display: none; rule to your CSS. In cases where you are loading angular.js in the body or templates aren't compiled soon enough, use the ng-cloak directive and include the following in your CSS:
/*
Allow angular.js to be loaded in body, hiding cloaked elements until
templates compile. The !important is important given that there may be
other selectors that are more specific or come later and might alter display.
*/
[ng\:cloak], [ng-cloak], .ng-cloak {
display: none !important;
}
As mentioned in the comment, the !important is important. For example, if you have the following markup
<ul class="nav">
<li><a href="/foo" ng-cloak>{{bar}}</a></li>
</ul>
and you happen to be using bootstrap.css, the following selector is more specific for your ng-cloak'ed element
.nav > li > a {
display: block;
}
So if you include a rule with simply display: none;, Bootstrap's rule will take precedence and the display will be set to block, so you'll see the flicker before the template compiles.
As mentioned in the documentation, you should add a rule to your CSS to hide it based on the ng-cloak attribute:
[ng\:cloak], [ng-cloak], .ng-cloak {
display: none;
}
We use similar tricks on the "Built with Angular" site, which you can view the source of on Github: https://github.com/angular/builtwith.angularjs.org
Hope that helps!
Make sure AngularJS is included in the head of the HTML. See ngCloak doc:
For the best result, angular.js script must be loaded in the head
section of the html file; alternatively, the css rule (above) must be
included in the external stylesheet of the application.
I've never had much luck using ngCloak. I still get flickering despite everything mentioned above. The only surefire way to avoid flicking is to put your content in a template and include the template. In a SPA, the only HTML that will get evaluated before being compiled by Angular is your main index.html page.
Just take everything inside the body and stick it in a separate file and then:
<ng-include src="'views/indexMain.html'"></ng-include>
You should never get any flickering that way as Angular will compile the template before adding it to the DOM.
ngBind and ngBindTemplate are alternatives that do not require CSS:
<div ng-show="foo != null" ng-cloak>{{name}}</div> <!-- requires CSS -->
<div ng-show="foo != null" ng-bind="name"></div>
<div ng-show="foo != null" ng-bind-template="name = {{name}}"></div>
In addition to the accepted answer if you're using an alternative method of triggering ng-cloak...
You may also wish to add some additional specificities to your CSS/LESS:
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak],
.ng-cloak, .x-ng-cloak,
.ng-hide {
display: none !important;
}
I had a similar issue and found out that if you have a class that contains transitions, the element will blink. I tried to add ng-cloak without success, but by removing the transition the button stopped blinking.
I'm using ionic framework and the button-outline has this transition
.button-outline {
-webkit-transition: opacity .1s;
transition: opacity .1s;
}
Simply overwrite the class to remove the transition and the button will stop blinking.
Update
Again on ionic there is a flicker when using ng-show/ng-hide. Adding the following CSS resolves it:
.ng-hide-add,
.ng-hide-remove {
display: none !important;
}
Source: http://forum.ionicframework.com/t/beta-14-ng-hide-show/14270/9
I had a problem where a <div ng-show="expression"> would be initially visible for a fraction of a second, even though "expression" was initially false, before the ng-show directive got a chance to run.
The solution I used was to manually add the "ng-hide" class, as in <div ng-show="expression" ng-hide>, to make sure it started initially hidden. The ng-show directive will add/remove the ng-hide class as necessary after that.
Try to turn off the Firebug. I'm serious. This helps in my case.
Apply accepted answer and then make sure that Firebug in your Firefox is turned off: press F12 then turn off Firebug for this page - small button in upper right corner.
There's actually two separate problems that can cause the flicker issue and you could be facing either one or both of these.
Problem 1: ng-cloak is applied too late
This issue is solved as descibed in many of the answers on this page is to make sure AngularJS is loaded in the head. See ngCloak doc:
For the best result, angular.js script must be loaded in the head
section of the html file; alternatively, the css rule (above) must be
included in the external stylesheet of the application.
Problem 2: ng-cloak is removed too soon
This issue is most likely to occur when you have a lot of CSS on your page with rules cascading over one another and the deeper layers of CSS flash up before the top layer is applied.
The jQuery solutions in answers involving adding style="display:none" to your element do solve this issue so long as the style is removed late enough (in fact these solutions solve both problems). However, if you prefer not to add styles directly to your HTML you can achieve the same results using ng-show.
Starting with the example from the question:
<ul ng-show="foo != null" ng-cloak>..</ul>
Add an additional ng-show rule to your element:
<ul ng-show="isPageFullyLoaded && (foo != null)" ng-cloak>..</ul>
(You need to keep ng-cloak to avoid problem 1).
Then in your app.run set isPageFullyLoaded:
app.run(['$rootScope', function ($rootScope) {
$rootScope.$safeApply = function (fn) {
$rootScope.isPageFullyLoaded = true;
}
}]);
Be aware that depending on exactly what you're doing, app.run may or may not be the best place to set isPageFullyLoaded. The important thing is to make sure that isPageFullyLoaded gets set to true after whatever it is you don't want to flicker is ready to be revealed to the user.
It sounds like Problem 1 is the problem the OP is hitting, but others are finding that solution does not work or only partially works because they are hitting Problem 2 instead or as well.
Important Note: Be sure to apply solutions to both ng-cloak being applied too late AND removed to soon. Solving only one of these problems may not relieve the symptoms.
We ran into this problem at our company and solved it by adding "display: none" to the CSS styling for those flickering ng-show elements. We didn't have to use ng-cloak at all. Unlike others in this thread, we experienced this issue in Safari but not Firefox or Chrome -- possibly due to Safari's lazy repaint bug in iOS7.
For what it's worth, I had a similar issue ng-cloak not working. It may be worth checking your app/site with cache enabled to reuse source files to see if that helps.
With my run-in with flickering, I was testing with DevTools open and cache disabled. Leaving the panel closed with caching enabled fixed my issue.
Keeping the below statements in head tag fixed this issue
<style type="text/css">
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
display: none !important;
}
</style>
official documentation
I couldn't get any of these methods to work, so I eventually did the following. For the element you want initially hidden:
<div <!--...--> style="display: none;" ng-style="style">
Then in your controller, add this at or near the top:
$scope.style = { display: 'block' };
This way, the element is initially hidden, and only shows when the controller code has actually run.
You can tune the placement to your needs, perhaps adding some logic. In my case, I switch to a login path if not currently logged in, and didn't want my interface flickering beforehand, so I set $scope.style after the logged-in check.
It's better to use ng-if instead of ng-show. ng-if completely removes and recreates the element in the DOM and helps to avoid ng-shows blinking.
I'm using ionic framework, adding css style might not work for certain circumstances, you can try using ng-if instead of ng-show / ng-hide
ref: https://books.google.com.my/books?id=-_5_CwAAQBAJ&pg=PA138&lpg=PA138&dq=ng-show+hide+flickering+in+ios&source=bl&ots=m2Turk8DFh&sig=8hNiWx26euGR2k_WeyaVzdixJ8k&hl=en&sa=X&redir_esc=y#v=onepage&q=ng-show%20hide%20flickering%20in%20ios&f=false
you'd better reference angular document, becuase the version[1.4.9] has update to below that make it could support data-ng-cloak directive.
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
display: none !important;
}
I tried the ng-cloak solution above but it still has intermittent issues with Firefox.
The only workaround that worked for me is to delay the loading of the form until Angular is fully loaded.
I simply added ng-init in the app and use ng-if at the form side to check if the variable I set is already loaded.
<div ng-app="myApp" ng-init="loaded='yes'">
<form ng-if="loaded.length > 0">
<!--all my elements here-->
</form>
</div>
In addition to other answers, if you find the flash of template code to still be occuring it is likely you have your scripts at the bottom of the page and that means that the ng-cloak directive straight up will not work. You can either move your scripts to the head or create a CSS rule.
The docs say "For the best result, the angular.js script must be loaded in the head section of the html document; alternatively, the css rule above must be included in the external stylesheet of the application."
Now, it doesn't have to be an external stylesheet but just in a element in the head.
<style type="text/css">
.ng-cloak {
display: none !important;
}
</style>
source: https://docs.angularjs.org/api/ng/directive/ngCloak
I tried every solution posted here and still got flickering in Firefox.
If it helps anyone, I solved it by adding style="display: none;" to the main content div, then using jQuery (I was already using it on the page) $('#main-div-id').show(); once everything was loaded after getting data from the server;
I actually found the suggestion from Rick Strahl's Web Log fixed my issue perfectly (as I still had the odd issue with ng-cloak blinking raw {{code}} at times, especially while running Firebug):
The nuclear option: Hiding the Content manually
Using the explicit CSS is the best choice, so the following shouldn’t ever be necessary. But I’ll mention it here as it gives some insight how you can hide/show content manually on load for other frameworks or in your own markup based templates.
Before I figured out that I could explicitly embed the CSS style into the page, I had tried to figure out why ng-cloak wasn’t doing its job. After wasting an hour getting nowhere I finally decided to just manually hide and show the container. The idea is simple – initially hide the container, then show it once Angular has done its initial processing and removal of the template markup from the page.
You can manually hide the content and make it visible after Angular has gotten control. To do this I used:
<div id="mainContainer" class="mainContainer boxshadow"
ng-app="app" style="display:none">
Notice the display: none style that explicitly hides the element initially on the page.
Then once Angular has run its initialization and effectively processed the template markup on the page you can show the content. For Angular this ‘ready’ event is the app.run() function:
app.run( function ($rootScope, $location, cellService) {
$("#mainContainer").show();
…
});
This effectively removes the display:none style and the content displays. By the time app.run() fires the DOM is ready to displayed with filled data or at least empty data – Angular has gotten control.
I tried all of the answers above and nothing worked. Using ionic to develop a hybrid app and was trying to make an error message not flicker. I ended up solving my issue by adding an ng-hide class to my element. My div already has ng-show to show the element when there is an error. Adding ng-hide set the div to not display before angular is loaded. No ng-cloak or adding angular to the head necessary.
AS from the above discussion
[ng-cloak] {
display: none;
}
is the perfect way to solve the Problem.
I'm using ng-show in a directive to show and hide popups.
<div class="..." ng-show="showPopup">
None of the above worked for me, and using ng-if instead of ng-show would be an overkill. That would imply removing and adding the whole popup content into the DOM at every single click. Instead I added an ng-if into the same element to make sure it doesn't show at the document load:
<div class="..." ng-show="showPopup" ng-if="popupReady">
Afterwards I added the initialization into the controller responsible of this directive with a timeout:
$timeout(function () {
$scope.popupReady = true;
});
This way I eliminated the flickering issue and avoided the costly operation of DOM insertion at every single click. This came at an expense of using two scope variables for the same purpose instead of one, but so far this is definitely the best option.
Tried ng-cloak but still brief blinks. Below code rid them completely.
<body style="display:none;" ng-style="{'display':'block'}">
None of the solutions listed above worked for me. I then decided to look at the actual function and realised that when “$scope.watch ” was fired, it was putting a value in the name field which was not meant to be the case. So in the code I set and oldValue and newValue then
$scope.$watch('model.value', function(newValue, oldValue) {
if (newValue !== oldValue) {
validateValue(newValue);
}
});
Essentially when scope.watch is fired in this case, AngularJS monitors the changes to the name variable (model.value)
I would would wrap the <ul> with a <div ng-cloak>
I personally decided to use the ng-class attribute rather than the ng-show. I've had a lot more success going this route especially for pop-up windows that are always not shown by default.
What used to be <div class="options-modal" ng-show="showOptions"></div>
is now: <div class="options-modal" ng-class="{'show': isPrintModalShown}">
with the CSS for the options-modal class being display: none by default. The show class contains the display:block CSS.
Avoid line break
<meta http-equiv="Content-Security-Policy" content="
default-src 'FOO';
script-src 'FOO';
style-src 'FOO';
font-src 'FOO';">
Works with Firefox 45.0.1
<meta http-equiv="Content-Security-Policy" content=" default-src 'FOO'; script-src 'FOO'; style-src 'FOO'; font-src 'FOO';">

Categories

Resources