I have created a simple app using AngularJS. When I open the page for a second I see the screen below:
However, after the load is complete I see the loaded and styled content which is fine:
How do I prevent the flash of AngularJS code on my page ? Is this related to FOUC ?
Here is the HTML code:
<!doctype html>
<html class="no-js" lang="en" ng-app="MainApp">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Foundation | Welcome</title>
<link rel="stylesheet" href="css/foundation.css" />
<script src="js/vendor/modernizr.js"></script>
<style>
.row.full-width {
width: 100%;
margin-left: auto;
margin-right: auto;
max-width: initial;
}
</style>
</head>
<body ng-controller="MainCtrl">
<div class="off-canvas-wrap" data-offcanvas>
<div class="inner-wrap">
<nav class="tab-bar">
<section class="right-small">
<a class="right-off-canvas-toggle menu-icon" href="#"><span></span></a>
</section>
<section class="left tab-bar-section">
<h1 class="title">Salary Calculator</h1>
</section>
</nav>
<aside class="right-off-canvas-menu">
<ul class="off-canvas-list">
<li>
<label>Location</label>
</li>
<li>United Kingdom
</li>
</ul>
</aside>
<section class="main-section">
<div class="row full-width">
<div class="large-4 columns">
<ul class="tabs" data-tab>
<li class="tab-title active">Annual Salary
</li>
<li class="tab-title">Monthly Expenses
</li>
</ul>
<div class="tabs-content">
<div class="content active" id="panel1">
<div class="row">
<div class="large-12 columns">
<input ng-change="calculate()" type="text" placeholder="Salary" ng-model="salary"/>
</div>
</div>
</div>
<div class="content" id="panel2">
<div class="row">
<div class="large-4 columns">
<input ng-change="calculate()" type="text" placeholder="Rent" ng-model="rent" />
</div>
<div class="large-4 columns">
<input ng-change="calculate()" type="text" placeholder="Wireless, TV, Home Phone" ng-model="telecom"/>
</div>
<div class="large-4 columns">
<input ng-change="calculate()" type="text" placeholder="TV License" ng-model="tv" />
</div>
</div>
<div class="row">
<div class="large-4 columns">
<input ng-change="calculate()" type="text" placeholder="Mobile Phone" ng-model="mobile"/>
</div>
<div class="large-4 columns">
<input ng-change="calculate()" type="text" placeholder="Subscription" ng-model="subscription"/>
</div>
<div class="large-4 columns">
<input ng-change="calculate()" type="text" placeholder="Electricty" ng-model="electricity" />
</div>
</div>
<div class="row">
<div class="large-4 columns">
<input ng-change="calculate()" type="text" placeholder="Food" ng-model="food"/>
</div>
<div class="large-4 columns">
<input ng-change="calculate()" type="text" placeholder="Transport" ng-model="transport" />
</div>
<div class="large-4 columns">
<input ng-change="calculate()" type="text" placeholder="Charity" ng-model="charity"/>
</div>
</div>
<div class="row">
<div class="large-12 columns">
<input ng-change="calculate()" type="text" placeholder="Other" ng-model="other"/>
</div>
</div>
</div>
</div>
</div>
<div class="large-8 columns" ng-cloak >
<table >
<thead>
<tr>
<th width="200"></th>
<th width="250">Yearly</th>
<th width="250">Monthly</th>
<th width="250">Weekly</th>
<th width="250">Daily</th>
</tr>
</thead>
<tbody ng-repeat="val in results">
<tr>
<td>{{val.rowType}}</td>
<td>{{val.yearly}}</td>
<td>{{val.monthly}}</td>
<td>{{val.weekly}}</td>
<td>{{val.daily}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<a class="exit-off-canvas"></a>
</div>
</div>
<script src="../bower_components/angularjs/angular.js"></script>
<script src="js/app-service.js"></script>
<script src="js/app-controller.js"></script>
<script src="js/app-directives.js"></script>
<script src="js/app.js"></script>
<script src="js/vendor/jquery.js"></script>
<script src="js/foundation.min.js"></script>
<script>
$(document).foundation();
</script>
</body>
</html>
EDIT:
Please see my answer as well for an alternative solution in addition to the accepted one.
ng-cloak will help to some extent, but you can fully prevent it using ng-bind directive instead of using {{ }}.
e.g.
<td ng-bind="val.monthly"> </td>
not
<td>{{val.monthly}}</td>
It has been a long time but here is for my working solution for this one:
You need to use ng-cloak on the body tag of your html BUT the most important part is this CSS below:
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
display: none !important;
}
For me I had to add this for getting ng-cloak to work. This is probably not the only solution to this problem as can be seen in other answers. Hope this helps someone.
Angular already gives you the tool to prevent this: ngCloak: https://docs.angularjs.org/api/ng/directive/ngCloak
Just put the directive on your body like <body ng-cloak> and it should work.
EDIT
The Docs also advice you to actually not put it on the body, but on smaller portions of your page - wherever you see the need. Depending on the size of your page, that is a good idea. For smaller Pages I put it on the body and never had problems.
Along with ng-cloak, you can use a resolve object in your router. This will prevent the controller from instantiating and the view from rendering until the data is there.
In the following example I am assuming you are using uiRouter. The same pattern applies for ngRouter.
Your state config:
$stateProvider
.state('yourState',{
templateUrl: 'yourTemplate.html',
controller: 'YourController as vm',
resolve: YourController.resolve
})
As you can see, you have set the resolve property of the state to a static resolve object on your controller. Now the route will not resolve until this object is resolved.
To setup resolve object, lets assume you have a service yourService that has a method getData that returns a promise. This is very important. Because we don't want the route resolved until the promise is resolved.
So your controller may look something like this.
YourController.$inject = ['yourService'];
function YourController(yourService) {
var self = this;
yourService.getData().then((data) { self.data = data});
}
This is pretty standard. You can access the data from the view with vm.data but you will see a flash of {{vm.data}}. That is, if we remove the resolve we have added to the state config.
So now we change the controller to add a static resolve object to work with the resolve we have added to the state config.
YourController.resolve = {
'yourService': 'yourService',
'data': ['yourService', function(yourService) {
return yourService.getData();
}]
}
YourController.$inject = ['data'];
function YourController(data) {
this.data = data;
}
So now we have a resolve object. The yourService will resolve as a normal service, but the data property will resolve only when the promise returned by getData() is resolved. Then this data will be passed directly into the controller using Dependancy Injection.
In reality, you probably wont need to use ng-cloak if you use resolve.
Here is a working example:
angular.module('app', ['ui.router'])
.config(['$stateProvider',
function($stateProvider) {
$stateProvider
.state('noDot', {
controller: "NoDotController",
template: "Using a old style $scope binding {{customers[0].CutomerName}}"
})
.state('noResolve', {
controller: "NoResolveController as vm",
template: "We are displaying things before the data is here {{vm.customers[0].CustomerName}}"
})
.state('withResolve', {
controller: "WithResolveController as vm",
template: "We are waiting for data before displaying anything {{vm.customers[0].CustomerName}}",
resolve: WithResolveController.resolve
})
.state('empty', {
template: ""
})
}
])
.controller('NoResolveController', NoResolveController)
.controller('NoDotController', NoDotController)
.controller('WithResolveController', WithResolveController)
.service('northwind', Northwind);
NoDotController.$inject = ['$scope', 'northwind'];
function NoDotController($scope, northwind) {
northwind.getCustomers().then(function(customers) {
$scope.customers = customers});
}
NoResolveController.$inject = ['northwind'];
function NoResolveController(northwind) {
var self = this;
northwind.getCustomers().then(function(customers) {
self.customers = customers;
});
}
WithResolveController.resolve = {
'northwind': 'northwind',
'customers': ['northwind',
function(northwind) {
return northwind.getCustomers();
}
]
}
WithResolveController.$inject = ['customers'];
function WithResolveController(customers) {
this.customers = customers;
}
Northwind.$inject = ['$timeout', '$q'];
function Northwind($timeout, $q) {
this.$q = $q;
this.$timeout = $timeout;
}
Northwind.prototype.getCustomers = function() {
var deferred = this.$q.defer();
this.$timeout(function() {
deferred.resolve([{CustomerName: "Name of Customer"}])
}, 1000);
return deferred.promise;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.13/angular-ui-router.js"></script>
<div ng-app="app">
<a ui-sref="noDot" href="#">No Dot</a>
<span> | </span>
<a ui-sref="empty" href="#">Emtpy</a>
<span> | </span>
<a ui-sref="noResolve" href="#">No Resolve</a>
<span> | </span>
<a ui-sref="empty" href="#">Emtpy</a>
<span> | </span>
<a ui-sref="withResolve" href="#">With Resolve</a>
<br>
<br>
<ui-view></ui-view>
</div>
Related
What it should do
Each button is linked to a div block (ex: button01 links to block01). Each div block has 3 inputs. The last input gets the value of the a math function between the two inputs above it. The div blocks must "appear" not show/hide.
What it does
The part where the three buttons open the three div blocks works. The problem is that when I try to type something inside the two inputs the last input gets nothing.
What I've tried
1) ng-switch and ng-switch-when
2) place the divs inside each (script type="text/ng-template" id="aaa")
and then;
var forms = ["aaa", "bbb", "ccc", "ddd"];
credit_block.displayedForms = [];
credit_block.addForm = function(formIndex) {
credit_block.displayedForms = [];
credit_block.displayedForms.push(forms[formIndex]);
}
3) (this one worked but it's not what I want) ng-show
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<div class="row">
<div class="col-sm-12">
Pick a topic:
<label><span>Button01</span><input style="display:none" type="radio" ng-model="myVar" value="block01"></label>
<label><span>Button02</span><input style="display:none" type="radio" ng-model="myVar" value="block02"></label>
<label><span>Button03</span><input style="display:none" type="radio" ng-model="myVar" value="block03"></label>
</div>
</div>
<div class="row" ng-switch="myVar">
<div class="col-sm-12" ng-switch-when="block01">
<h3>Cumpar locuinta</h3>
<div class="row">
<div class="col-sm-10 col-xs-8 number-box">
<label>Sum:</label>
<input class="button-option" type="number" ng-model="sum">
</div>
</div>
<div class="row">
<div class="col-sm-10 col-xs-8 number-box">
<label>Advancement:</label>
<input class="button-option" type="number" ng-model="adv">
</div>
</div>
<div class="row">
<div class="col-sm-10 col-xs-8 number-box">
<label>Value:</label>
<input class="button-option" type="number" value="{{ result() }}">
</div>
</div>
</div>
</div>
</div>
<script>
var myApp = angular.module('myApp', []);
myApp.controller('myController', function($scope) {
$scope.sum = 5;
$scope.adv = 4;
$scope.result = function() {
return $scope.sum - $scope.adv;
};
});
</script>
You've got a classic scope-related variable shadowing problem.
ng-switch creates a new scope, so your ng-model bindings to sum and adv don't correspond to the ones you've set up in myController.
A usual way to work around this newly created scope is to put ng-model properties for binding behind an object and reference them through it.
For example, in your HTML:
<input class="button-option" type="number" ng-model="data.sum">
Then in your controller:
$scope.data = { sum: ... };
Here's a demonstration of your snippet with some amendments to make it work.
I have a custom directive that I am going to put on a page multiple times
<div id="upcomingweekssubnav" style="text-align:center">
<div style="width:100%">
<div style="width: 50%; float:left">
<h4>Week:</h4>
<ul class="weeks">
<li ng-model="vm.selectedWeek" ng-repeat="n in vm.range(vm.selectedLeague.StartWeek,vm.selectedLeague.EndWeek)">
<span ng:click="vm.setWeeklyLineup(n)">{{n}}</span>
</li>
</ul>
</div>
</div>
</div>
<br /><br />
<div id="my-team-lineup" ng-show="vm.showMe" style="text-align:center">
<div id="myteamDiv" style="width:100%; overflow:hidden;">
<div id="myTeamBeforeDiv" style="width:50%; float: left;">
<center>
<h4>Before Trade</h4>
<div ng-lineup week="vm.selectedWeek" lineup="vm.myBeforeTradeLineup"></div>
</center>
</div>
<div id="myTeamAfterDiv " style="width:50%; float: right ">
<center>
<h4>After Trade</h4>
<div ng-lineup week="vm.selectedWeek" lineup="vm.myAfterTradeLineup"></div>
</center>
</div>
</div>
</div>
<div id="thier-team-lineup" ng-show="!vm.showMe" style="text-align:center">
<div id="thierteamDiv" style="width:100%; overflow:hidden;">
<div id="thierTeamBeforeDiv" style="width:50%; float: left">
<center>
<h4>Before Trade</h4>
<div ng-lineup week="vm.selectedWeek" lineup="vm.thierBeforeTradeLineup"></div>
</center>
</div>
<div id="thierTeamAfterDiv " style="width:50%; float: right ">
<center>
<h4>After Trade</h4>
<div ng-lineup week="vm.selectedWeek" lineup="vm.thierAfterTradeLineup"></div>
</center>
</div>
</div>
</div>
ng-lineup is the directive name, here is the javascript and template file
app.directive('ngLineup', function () {
var directive = {
link: function (scope, elem, attrs) {
scope.week = null;
scope.$watch(attrs.week, function (data) {
var myBefore = scope.$eval(attrs.lineup);
if (myBefore !== undefined) {
myBefore.forEach(function (element) {
if (element.Week === data) {
var roster = element.Roster;
element.Roster.forEach(function (player) {
if (player.WeeklyMatchups[data] !== undefined) {
player.WeekProjections = player.WeeklyMatchups[data].WeekProjections;
player.Opponent = player.WeeklyMatchups[data].Opponent;
}
}, this);
scope.lineup = roster;
scope.pointsTotal = element.ProjectedPoints;
}
}, this);
}
});
},
restrict: 'AE',
priority: 10,
templateUrl: '/app/templates/lineup.html',
};
return directive;
});
Template
<div class="tableRow header blue" id="statTable0">
<div class="cell">Pos</div>
<div class="cell">Players</div>
<div class="cell">Opp</div>
<div class="cell">Proj Pts</div>
</div>
<div class="tableRow" ng:repeat="e in lineup">
<div class="cell">
{{e.Name == "" ? 'No Player Available' : e.Name}}
</div>
<div class="cell">
{{e.Position.Abbreviation.indexOf("_") > -1 ? "FLEX" : e.Position.Abbreviation }}
</div>
<div class="cell">
{{e.Opponent}}
</div>
<div class="cell">
{{vm.selectedWeek == e.ByeWeek[0] ? 'BYE' : e.WeekProjections}}
</div>
</div>
<br />
<h4>Week {{vm.selectedWeek}} Projected Total (Before Trade): {{vm.myBeforeWeekProjectedPoints}}</h4>
And here is what the directive looks like on the screen
What I am doing is based on the selected Week, I am showing some data that is based off that scope variable. What is happening is that when the directive watch is triggered, all the directives on the page are being updated, so each one has the same data in it. I started reading on isolated scope, but I am having a hard time being able to wire that up with the watch that I need.
use scope:true
as
var directive = {
scope:true;
link: function (scope, elem, attrs) {
it will inherit from parent but won't reflect back any changes made from parent back to directive.
The problem is that vm.selectedWeek is shared as an week. Thus a change triggered in one of the directives would definitely trigger the watch in the other directives. Thus, although you changed the value at one, the change got reflected across. Try to use the bindings property and set it to
var directive = {
...
...
bindings:{
week: '<'
...
}
This will ensure only one-way binding
Also, instead of using attrs.week, try to use week directly from the controller scope. Mark as answer if this helps.
I'm using asp.net mvc razor
The page must show a very long list. Phonebook employees - about 500 people. Page is loading too slowly because of too many entities.
How to load first 50 entries, and later the others 450 entries - by background?
Thank you so much!
Code on page:
#model WarehouseSearch.Controllers.PhonesList
#{
string LastFirstChar = "";
}
<div id="main-content">
<div class="container">
<div class="row">
<div class="col-md-10">
<h2>Phonebook </h2>
</div>
<div class="col-md-4">
</div>
</div>
<div class="row">
<div class="col-md-8" style="border:1px solid #ddd;background-color:white">
<div class="row" style="background-color:rgba(247, 225, 181, 1);margin-bottom:1em;padding:0.5em;">
<div class="col-md-7 text-left" style="padding-left: 1em; padding-top: 0.2em; padding-right: 1em; padding-bottom: 0.3em;">
<span class="fa fa-user fa-2x blue"></span> <input type="text" name="Search" id="search_input" placeholder="search ..." class="search_input" />
</div>
<div class="col-md-3">
</div>
</div>
<ul id="phones" class="interleave">
#foreach (WarehouseSearch.GetPeoplePhonesListResult p in Model.allphones)
{
<li style="padding-top:2em;" class="row">
<div class="col-md-2">
#if (LastFirstChar != #p.FirstChar)
{
<span style="background-color:#41a7e2;color:white;position:relative;left:-4em;font-size:210%;padding:0.3em;" class="text-right"><b>#p.FirstChar</b></span>
LastFirstChar = p.FirstChar;
}
<img style="width: 85%; display: block;" src="#p.BigPhoto" /><br />
</div>
<div class="col-md-5">
<h3 class="phone smarttlink" style="margin-bottom:0.1em;margin-top:0;">#p.Family</h3>
<div>#p.FirstMiddleName</div>
<br />
<small style="color:#666">#p.Title</small>
</div>
.... some other info about people....
</li>
}
</ul>
</div>
</div>
</div>
</div>
<script>
$('#search_input').animate({
//"width": "100%",
//"padding": "4px",
"opacity": 1
}, 300, function () {
$(this).focus();
});
$(function () {
$(function () {
$('#search_input').fastLiveFilter('#phones'
, {
selector: ".phone, .phone2"
, callback: function (total) {
$('.phone').unhighlight();
searchTerm = $('#search_input').val();
if (searchTerm.length > 0) {
$('.phone').highlight(searchTerm);
}
}
, translit : true
}
);
});
});
</script>
in Controller:
:
public ActionResult List()
{
using (WarehouseSearch.sqldbDataContext sqldb = new WarehouseSearch.sqldbDataContext())
{
PhonesList pl = new PhonesList();
pl.allphones = sqldb.GetPeoplePhonesList().ToList<GetPeoplePhonesListResult>();
return View("~/Views/Home/PeoplePhones.cshtml", pl);
}
}
First I would recommend you to use an external CSS file instead of inline styling.
That way the CSS can be cached in the browser and might help you to boost the performance.
Also in a div with a class "row" you have only 12 columns,it should be like this.
<div class="row">
<div class="col-md-10">
<h2>Phonebook </h2>
</div>
<div class="col-md-2">
</div>
</div>
I would try change this Action to take the first 50
public ActionResult List()
{
using (WarehouseSearch.sqldbDataContext sqldb = new WarehouseSearch.sqldbDataContext())
{
PhonesList pl = new PhonesList();
pl.allphones = sqldb.GetPeoplePhonesList().Take(50).ToList<GetPeoplePhonesListResult>();
return View("~/Views/Home/PeoplePhones.cshtml", pl);
}
}
And later in the view after it finished to iterate over the top 50 phones
Call to another function from the controller that will retrieve the rest of the phones.
#{
((HomeController)this.ViewContext.Controller).GetLast450();
}
And iterate that, probably a partial view will be good here.
I am trying to build a directive that will show a loading graphic when doing ajax requests on a page.
I have set this up:
.directive('ajaxLoader', ['$http', function ($http) {
return {
restrict: 'A',
link: function (scope, element, attr) {
scope.isLoading = function () {
return $http.pendingRequests.length > 0;
};
scope.$watch(scope.isLoading, function (loading) {
if (!loading) {
element.addClass('ng-hide');
}
});
}
}
}]);
which works fine if there is only one ajax directive on a page.
The Html I currently have is like this:
<div class="fixed animated fadeIn">
<nav class="top-bar" role="navigation">
<!-- cut for brevity -->
<section ng-controller="ProfileController as controller">
<div class="loading" ajax-loader></div>
<ul ng-show="controller.user">
<li>
<a href="#/security/login">
<h1>
<span class="right profile-image">
</span>
<span ng-bind="controller.user.firstName"></span><br />
<span class="small" ng-bind="controller.user.lastName"></span>
</h1>
</a>
</li>
</ul>
</section>
</nav>
</div>
<div class="row" ng-controller="CustomerServicesController as controller">
<!-- cut for brevity -->
<section class="col-md-3">
<h2>Recent orders</h2>
<div class="row">
<div class="medium-12 columns data">
<div class="loading" ajax-loader></div>
<table ng-hide="controller.recent.length === 0">
<tr>
<th>Account</th>
<th>Raised by</th>
<th>Reference</th>
<th>Detail</th>
</tr>
<tr ng-repeat="order in controller.recent" id="{{order.orderNumber}}">
<td><strong ng-bind="order.account.accountNumber"></strong> <span ng-bind="order.account.name"></span></td>
<td ng-bind="order.raisedBy"></td>
<td ng-bind="order.orderNumber"></td>
<td ng-bind="order.description"></td>
</tr>
</table>
</div>
</div>
</section>
</div>
Now, what appears to happen is that both loaders stay up for the same amount of time (it's like they are waiting for the other to finish before they disappear). If I refresh the screen I see both loading graphics appear and then when both areas have finished their calls, both images disappear. I would like them to work independently, does anyone know how I can do this?
Also, on another smaller issue, the data seems to be loaded a split second before the images disappear. Is there a way to get them to show/hide at the same time? Or if not possible, get the loader to disappear before the data is loaded in the DOM?
Using C#, PowerShell and Jsoup I've tried to capturing the percentage that is displayed at the end, 99.96%. Using the DOM explorer in IE, this is what I see:
<!DOCTYPE html>
<html>
<body id="website#22;page">
<div id="wrap">
<script type="text/javascript">...</script>
<div class="topRow" id="iTopRow">...</div>
<div id="header">...</div>
<div id="content">
<div class="paddingfix">
<script type="text.javascript">...</script>
<div class="toppaddingfix"></div>
<div title="Summary Percentage"
class="overallPercentage">
<div>
<div style="padding-bottom: 12px;">
<div class="textRegionContainer" id="iTextRegion_D14">
<div class="position" style="display: none;"<>/div>
<div class="status" style="display: none;">loaded</div>
<div class="textRegionPlaceHolder">
<p class="textRegionTitle">
99.96 %
</p>
</div>
</div>
</div>
However, when programmatically trying to capture it I receive this:
<div id="iTextRegion_DR14" class="textRegionContainer">
<script type="text/javascript">
$(document).ready(function() {
var client = new wLive.Service.Client(false);
var textRegion = new wLive.Controls.TextRegion(
'Tools',
'AppServerOutDetails3',
'DR14',
'#iTextRegion_DR14',
{"id":"DR14","data":null,"paragraphs":["<bindingref
name=\"Reliability\" \/> %\u000d\u000a "],"style":0},
client);
wLive.Controls.DataBridge.registerDataRegion('#iDataBridge', textRegion);
});
</script>
</div>
How do I programmatically capture that percentage so I can then have another program evaluate it's value?